1 {
2  /***************************************************************************
3                           projectinspector.pas
4                           --------------------
5 
6 
7  ***************************************************************************/
8 
9  ***************************************************************************
10  *                                                                         *
11  *   This source is free software; you can redistribute it and/or modify   *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This code is distributed in the hope that it will be useful, but      *
17  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
19  *   General Public License for more details.                              *
20  *                                                                         *
21  *   A copy of the GNU General Public License is available on the World    *
22  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
23  *   obtain it by writing to the Free Software Foundation,                 *
24  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
25  *                                                                         *
26  ***************************************************************************
27 
28   Author: Mattias Gaertner
29 
30   Abstract:
31     TProjectInspectorForm is the form of the project inspector.
32 
33   ToDo:
34     - project groups:
35       - activate
36    popup menu:
37       - copy file name
38       - save
39       - options
40       - activate
41       - compile
42       - build
43       - view source
44       - close
45       - remove project
46       - build sooner Ctrl+Up
47       - build later Ctrl+Down
48       - compile all from here
49       - build all from here
50 }
51 unit ProjectInspector;
52 
53 {$mode objfpc}{$H+}
54 
55 interface
56 
57 uses
58   Classes, SysUtils,
59   // LCL
60   LCLType, LCLIntf, Forms, Controls, Buttons, ComCtrls, Menus, Dialogs,
61   ExtCtrls, StdCtrls, Graphics,
62   // LazControls
63   TreeFilterEdit,
64   // LazUtils
65   FileUtil, LazFileUtils, LazUtilities, LazLoggerBase, LazTracer, LazFileCache,
66   // Codetools
67   CodeToolsStructs, CodeToolManager, FileProcs, CodeCache, CodeTree, FindDeclarationTool,
68   // BuildIntf
69   ProjectIntf, PackageIntf, PackageLinkIntf, PackageDependencyIntf,
70   // IDEIntf
71   IDEHelpIntf, IDECommands, IDEDialogs, IDEImagesIntf, LazIDEIntf, ToolBarIntf,
72   // IDE
73   LazarusIDEStrConsts, MainBase, IDEProcs, DialogProcs, IDEOptionDefs, Project,
74   InputHistory, TransferMacros, EnvironmentOpts, BuildManager, BasePkgManager,
75   ProjPackChecks, ProjPackEditing, ProjPackFilePropGui, PackageDefs,
76   AddToProjectDlg, AddPkgDependencyDlg, AddFPMakeDependencyDlg, LResources;
77 
78 type
79   TOnAddUnitToProject =
endernull80     function(Sender: TObject; AnUnitInfo: TUnitInfo): TModalresult of object;
81   TRemoveProjInspFileEvent =
endernull82     function(Sender: TObject; AnUnitInfo: TUnitInfo): TModalResult of object;
endernull83   TRemoveProjInspDepEvent = function(Sender: TObject;
84                            ADependency: TPkgDependency): TModalResult of object;
endernull85   TAddProjInspDepEvent = function(Sender: TObject;
86                            ADependency: TPkgDependency): TModalResult of object;
87 
88   { TProjectInspectorForm }
89 
90   TProjectInspectorForm = class(TForm,IFilesEditorInterface)
91     AddPopupMenu: TPopupMenu;
92     FilterPanel: TPanel;
93     DirectoryHierarchyButton: TSpeedButton;
94     FilterEdit: TTreeFilterEdit;
95     PropsGroupBox: TGroupBox;
96     MenuItem1: TMenuItem;
97     MenuItem2: TMenuItem;
98     mnuAddFPMakeReq: TMenuItem;
99     mnuAddEditorFiles: TMenuItem;
100     mnuAddDiskFile: TMenuItem;
101     mnuAddReq: TMenuItem;
102     OpenButton: TSpeedButton;
103     ItemsTreeView: TTreeView;
104     ItemsPopupMenu: TPopupMenu;
105     SortAlphabeticallyButton: TSpeedButton;
106     Splitter1: TSplitter;
107     // toolbar
108     ToolBar: TToolBar;
109     // toolbuttons
110     AddBitBtn: TToolButton;
111     RemoveBitBtn: TToolButton;
112     OptionsBitBtn: TToolButton;
113     HelpBitBtn: TToolButton;
114     procedure CopyMoveToDirMenuItemClick(Sender: TObject);
115     procedure DirectoryHierarchyButtonClick(Sender: TObject);
116     procedure FilterEditKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
117     procedure FormActivate(Sender: TObject);
118     procedure FormCreate(Sender: TObject);
119     procedure FormDeactivate(Sender: TObject);
120     procedure FormDestroy(Sender: TObject);
121     procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
122     procedure ItemsPopupMenuPopup(Sender: TObject);
123     procedure ItemsTreeViewAdvancedCustomDrawItem(Sender: TCustomTreeView;
124       Node: TTreeNode; {%H-}State: TCustomDrawState; Stage: TCustomDrawStage;
125       var {%H-}PaintImages, {%H-}DefaultDraw: Boolean);
126     procedure ItemsTreeViewDblClick(Sender: TObject);
127     procedure ItemsTreeViewDragDrop(Sender, Source: TObject; X, Y: Integer);
128     procedure ItemsTreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
129       State: TDragState; var Accept: Boolean);
130     procedure ItemsTreeViewKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
131     procedure ItemsTreeViewSelectionChanged(Sender: TObject);
132     procedure mnuAddDiskFileClick(Sender: TObject);
133     procedure mnuAddEditorFilesClick(Sender: TObject);
134     procedure mnuAddFPMakeReqClick(Sender: TObject);
135     procedure mnuAddReqClick(Sender: TObject);
136     procedure mnuOpenFolderClick(Sender: TObject);
137     procedure MoveDependencyUpClick(Sender: TObject);
138     procedure MoveDependencyDownClick(Sender: TObject);
139     procedure SetDependencyDefaultFilenameMenuItemClick(Sender: TObject);
140     procedure SetDependencyPreferredFilenameMenuItemClick(Sender: TObject);
141     procedure ClearDependencyFilenameMenuItemClick(Sender: TObject);
142     procedure OpenButtonClick(Sender: TObject);
143     procedure OptionsBitBtnClick(Sender: TObject);
144     procedure HelpBitBtnClick(Sender: TObject);
145     procedure ReAddMenuItemClick(Sender: TObject);
146     procedure RemoveBitBtnClick(Sender: TObject);
147     procedure RemoveNonExistingFilesMenuItemClick(Sender: TObject);
148     procedure SortAlphabeticallyButtonClick(Sender: TObject);
149     procedure EnableI18NForLFMMenuItemClick(Sender: TObject);
150     procedure DisableI18NForLFMMenuItemClick(Sender: TObject);
151   private
152     FIdleConnected: boolean;
153     FOnAddDependency: TAddProjInspDepEvent;
154     FOnAddUnitToProject: TOnAddUnitToProject;
155     FOnCopyMoveFiles: TNotifyEvent;
156     FOnDragDropTreeView: TDragDropEvent;
157     FOnDragOverTreeView: TOnDragOverTreeView;
158     FOnReAddDependency: TAddProjInspDepEvent;
159     FOnRemoveDependency: TRemoveProjInspDepEvent;
160     FOnRemoveFile: TRemoveProjInspFileEvent;
161     FOnShowOptions: TNotifyEvent;
162     FShowDirectoryHierarchy: boolean;
163     FSortAlphabetically: boolean;
164     FUpdateLock: integer;
165     FLazProject: TProject;
166     FFilesNode: TTreeNode;
167     FNextSelectedPart: TObject;// select this file/dependency on next update
168     FDependenciesNode: TTreeNode;
169     FRemovedDependenciesNode: TTreeNode;
170     ImageIndexFiles: integer;
171     ImageIndexProject: integer;
172     ImageIndexUnit: integer;
173     ImageIndexRegisterUnit: integer;
174     ImageIndexText: integer;
175     ImageIndexBinary: integer;
176     ImageIndexDirectory: integer;
177     FFlags: TPEFlags;
178     FPropGui: TProjPackFilePropGui;
179     procedure AddMenuItemClick(Sender: TObject);
AddOneFilenull180     function AddOneFile(aFilename: string): TModalResult;
181     procedure DoAddMoreDialog;
182     procedure DoAddDepDialog;
183     procedure DoAddFPMakeDepDialog;
CreateToolButtonnull184     function CreateToolButton(AName, ACaption, AHint, AImageName: String;
185       AOnClick: TNotifyEvent): TToolButton;
CreateDividernull186     function CreateDivider: TToolButton;
187     procedure SetDependencyDefaultFilename(AsPreferred: boolean);
188     procedure SetIdleConnected(AValue: boolean);
189     procedure SetLazProject(const AValue: TProject);
190     procedure SetShowDirectoryHierarchy(const AValue: boolean);
191     procedure SetSortAlphabetically(const AValue: boolean);
192     procedure SetupComponents;
GetDependencyToUpdatenull193     function GetDependencyToUpdate(Immediately: boolean): TPkgDependencyID;
GetSingleSelectedDependencynull194     function GetSingleSelectedDependency: TPkgDependency;
195     procedure ApplyDependencyButtonClick(Sender: TObject);
TreeViewGetImageIndexnull196     function TreeViewGetImageIndex({%H-}Str: String; Data: TObject; var {%H-}AIsEnabled: Boolean): Integer;
197     procedure ProjectBeginUpdate(Sender: TObject);
198     procedure ProjectEndUpdate(Sender: TObject; ProjectChanged: boolean);
199     procedure EnableI18NForSelectedLFM(TheEnable: boolean);
200     procedure PackageListAvailable(Sender: TObject);
CanUpdatenull201     function CanUpdate(Flag: TPEFlag; Immediately: boolean): boolean;
202     procedure UpdateFiles(Immediately: boolean = false);
203     procedure UpdateProperties(Immediately: boolean = false);
204     procedure UpdateButtons(Immediately: boolean = false);
205     procedure UpdatePending;
206   protected
207     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
208     procedure IdleHandler(Sender: TObject; var {%H-}Done: Boolean);
209   public
210     constructor Create(TheOwner: TComponent); override;
211     destructor Destroy; override;
IsUpdateLockednull212     function IsUpdateLocked: boolean; inline;
213     procedure UpdateTitle(Immediately: boolean = false);
214     procedure UpdateRequiredPackages(Immediately: boolean = false);
TreeViewToInspectornull215     function TreeViewToInspector(TV: TTreeView): TProjectInspectorForm;
216   public
217     // IFilesEditorInterface
218     procedure BeginUpdate;
219     procedure EndUpdate;
220     procedure UpdateAll(Immediately: boolean = false);
GetNodeItemnull221     function GetNodeItem(NodeData: TPENodeData): TObject;
GetNodeDataItemnull222     function GetNodeDataItem(TVNode: TTreeNode; out NodeData: TPENodeData;
223       out Item: TObject): boolean;
ExtendIncSearchPathnull224     function ExtendIncSearchPath(NewIncPaths: string): boolean;
ExtendUnitSearchPathnull225     function ExtendUnitSearchPath(NewUnitPaths: string): boolean;
FilesBaseDirectorynull226     function FilesBaseDirectory: string;
FilesEditFormnull227     function FilesEditForm: TCustomForm;
FilesEditTreeViewnull228     function FilesEditTreeView: TTreeView;
FilesOwnernull229     function FilesOwner: TObject;
FilesOwnerNamenull230     function FilesOwnerName: string;
FilesOwnerReadOnlynull231     function FilesOwnerReadOnly: boolean;
FirstRequiredDependencynull232     function FirstRequiredDependency: TPkgDependency;
GetNodeFilenamenull233     function GetNodeFilename(Node: TTreeNode): string;
IsDirectoryNodenull234     function IsDirectoryNode(Node: TTreeNode): boolean;
TVNodeFilesnull235     function TVNodeFiles: TTreeNode;
TVNodeRequiredPackagesnull236     function TVNodeRequiredPackages: TTreeNode;
237   public
238     property LazProject: TProject read FLazProject write SetLazProject;
239     property OnShowOptions: TNotifyEvent read FOnShowOptions write FOnShowOptions;
240     property OnAddUnitToProject: TOnAddUnitToProject read FOnAddUnitToProject
241                                                      write FOnAddUnitToProject;
242     property OnAddDependency: TAddProjInspDepEvent
243                              read FOnAddDependency write FOnAddDependency;
244     property OnRemoveFile: TRemoveProjInspFileEvent read FOnRemoveFile
245                                                     write FOnRemoveFile;
246     property OnRemoveDependency: TRemoveProjInspDepEvent
247                              read FOnRemoveDependency write FOnRemoveDependency;
248     property OnReAddDependency: TAddProjInspDepEvent
249                              read FOnReAddDependency write FOnReAddDependency;
250     property OnDragDropTreeView: TDragDropEvent read FOnDragDropTreeView
251                                                       write FOnDragDropTreeView;
252     property OnDragOverTreeView: TOnDragOverTreeView read FOnDragOverTreeView
253                                                       write FOnDragOverTreeView;
254     property OnCopyMoveFiles: TNotifyEvent read FOnCopyMoveFiles
255                                            write FOnCopyMoveFiles;
256     property SortAlphabetically: boolean read FSortAlphabetically write SetSortAlphabetically;
257     property ShowDirectoryHierarchy: boolean read FShowDirectoryHierarchy write SetShowDirectoryHierarchy;
258     property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
259   end;
260 
261   { TSetBuildModeToolButton }
262 
263   TSetBuildModeToolButton = class(TIDEToolButton)
264   public type
265     TBuildModeMenuItem = class(TMenuItem)
266     public
267       BuildModeIndex: Integer;
268       procedure Click; override;
269     end;
270 
271     TBuildModeMenu = class(TPopupMenu)
272     protected
273       procedure DoPopup(Sender: TObject); override;
274     end;
275   public
276     procedure DoOnAdded; override;
277   end;
278 
279 var
280   ProjInspector: TProjectInspectorForm = nil;
281 
282 
UpdateUnitInfoResourceBaseClassnull283 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
284 
285 
286 implementation
287 
288 {$R *.lfm}
289 
UpdateUnitInfoResourceBaseClassnull290 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
291 var
292   LFMFilename, LFMType, Ancestor, LFMClassName, LFMComponentName: String;
293   LFMCode, Code: TCodeBuffer;
294   LoadFileFlags: TLoadBufferFlags;
295   ClearOldInfo: Boolean;
296   Tool: TCodeTool;
297   Node: TCodeTreeNode;
298   ListOfPFindContext: TFPList;
299   i: Integer;
300   Context: PFindContext;
301 begin
302   Result:=false;
303   if AnUnitInfo.Component<>nil then
304     exit(true); // a loaded resource is always uptodate
305   if AnUnitInfo.IsVirtual then
306     exit(true); // a new unit is always uptodate
307   ListOfPFindContext:=nil;
308   ClearOldInfo:=true;
309   try
310     // find lfm file
311     if not FilenameIsPascalUnit(AnUnitInfo.Filename) then
312       exit(true); // not a unit -> clear info
313     LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(
314       AnUnitInfo.Filename,true);
315     if (LFMFilename='') or not FileExistsCached(LFMFilename) then
316       exit(true); // no lfm -> clear info
317   finally
318     if ClearOldInfo then begin
319       AnUnitInfo.ResourceBaseClass:=pfcbcNone;
320       AnUnitInfo.ComponentName:='';
321       AnUnitInfo.ComponentResourceName:='';
322     end;
323   end;
324   try
325     if not FilenameExtIs(LFMFilename,'lfm',true) then
326       exit(true);          // no lfm format -> keep old info
327     // clear old info
328     AnUnitInfo.ResourceBaseClass:=pfcbcNone;
329     AnUnitInfo.ComponentName:='';
330     AnUnitInfo.ComponentResourceName:='';
331     // load lfm
332     LoadFileFlags:=[lbfUpdateFromDisk,lbfCheckIfText];
333     if Quiet then
334       Include(LoadFileFlags,lbfQuiet);
335     if LoadCodeBuffer(LFMCode,LFMFilename,LoadFileFlags,false)<>mrOk then
336       exit; // lfm read error
337     // read lfm header
338     ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
339     if LFMClassName='' then
340       exit; // lfm syntax error
341 
342     // LFM component name
343     AnUnitInfo.ComponentName:=LFMComponentName;
344     AnUnitInfo.ComponentResourceName:=LFMComponentName;
345 
346     // check ancestors
347     if LoadCodeBuffer(Code,AnUnitInfo.Filename,LoadFileFlags,false)<>mrOk then
348       exit; // pas read error
349     CodeToolBoss.Explore(Code,Tool,false,true);
350     if Tool=nil then
351       exit; // pas load error
352     try
353       Node:=Tool.FindDeclarationNodeInInterface(LFMClassName,true);
354       if Node=nil then
355         exit(Tool.FindImplementationNode<>nil); // class not found, reliable if whole interface was read
356 
357       if (Node.Desc<>ctnTypeDefinition)
358       or (Node.FirstChild=nil) or (Node.FirstChild.Desc<>ctnClass) then
359         exit(true); // this is not a class
360       Tool.FindClassAndAncestors(Node.FirstChild,ListOfPFindContext,false);
361       if ListOfPFindContext=nil then
362         exit; // ancestor not found -> probably syntax error
363 
364       for i:=0 to ListOfPFindContext.Count-1 do begin
365         Context:=PFindContext(ListOfPFindContext[i]);
366         Ancestor:=UpperCase(Context^.Tool.ExtractClassName(Context^.Node,false));
367         if (Ancestor='TFORM') then begin
368           AnUnitInfo.ResourceBaseClass:=pfcbcForm;
369           Result:=true;
370           Break;
371         end else if (Ancestor='TCUSTOMFORM') then begin
372           AnUnitInfo.ResourceBaseClass:=pfcbcCustomForm;
373           Result:=true;
374           Break;
375         end else if Ancestor='TDATAMODULE' then begin
376           AnUnitInfo.ResourceBaseClass:=pfcbcDataModule;
377           Result:=true;
378           Break;
379         end else if (Ancestor='TFRAME') or (Ancestor='TCUSTOMFRAME') then begin
380           AnUnitInfo.ResourceBaseClass:=pfcbcFrame;
381           Result:=true;
382           Break;
383         end else if Ancestor='TCOMPONENT' then begin
384           Result:=true;
385           Break;
386         end;
387       end;
388     except
389       exit; // syntax error or unit not found
390     end;
391     if not Result then exit;
392 
393     // Maybe auto-create it
394     //   (pfMainUnitHasCreateFormStatements in Project1.Flags)
395     //   and Project1.AutoCreateForms are checked by caller.
396     if (AnUnitInfo.ResourceBaseClass in [pfcbcForm,pfcbcCustomForm,pfcbcDataModule])
397     and (LFMComponentName<>'')
398     and (IDEMessageDialog(lisAddToStartupComponents,
399                           Format(lisShouldTheComponentBeAutoCreatedWhenTheApplicationS,
400                                  [LFMComponentName]),
401                           mtInformation,[mbYes,mbNo])=mrYes)
402     then
403       Project1.AddCreateFormToProjectFile(LFMClassName,LFMComponentName);
404   finally
405     FreeListOfPFindContext(ListOfPFindContext);
406   end;
407 end;
408 
409 
410 { TProjectInspectorForm }
411 
412 // inline
TProjectInspectorForm.IsUpdateLockednull413 function TProjectInspectorForm.IsUpdateLocked: boolean;
414 begin
415   Result:=FUpdateLock>0;
416 end;
417 
TVNodeFilesnull418 function TProjectInspectorForm.TVNodeFiles: TTreeNode;
419 begin
420   Result:=FFilesNode;
421 end;
422 
TProjectInspectorForm.TVNodeRequiredPackagesnull423 function TProjectInspectorForm.TVNodeRequiredPackages: TTreeNode;
424 begin
425   Result:=FDependenciesNode;
426 end;
427 
428 procedure TProjectInspectorForm.ItemsTreeViewDblClick(Sender: TObject);
429 begin
430   OpenButtonClick(Self);
431 end;
432 
433 procedure TProjectInspectorForm.ItemsTreeViewDragDrop(Sender, Source: TObject;
434   X, Y: Integer);
435 begin
436   OnDragDropTreeView(Sender,Source,X,Y);
437 end;
438 
439 procedure TProjectInspectorForm.ItemsTreeViewDragOver(Sender, Source: TObject;
440   X, Y: Integer; State: TDragState; var Accept: Boolean);
441 var
442   TargetTVNode: TTreeNode;
443   TargetTVType: TTreeViewInsertMarkType;
444 begin
445   if not OnDragOverTreeView(Sender,Source,X,Y, TargetTVNode, TargetTVType) then
446   begin
447     ItemsTreeView.SetInsertMark(nil,tvimNone);
448     Accept:=false;
449     exit;
450   end;
451 
452   if State=dsDragLeave then
453     ItemsTreeView.SetInsertMark(nil,tvimNone)
454   else
455     ItemsTreeView.SetInsertMark(TargetTVNode,TargetTVType);
456   Accept:=true;
457 end;
458 
459 procedure TProjectInspectorForm.ItemsTreeViewKeyDown(Sender: TObject;
460   var Key: Word; Shift: TShiftState);
461 var
462   Handled: Boolean;
463 begin
464   Handled := True;
465   try
466     if Key = VK_ESCAPE then
467       Close
468     else if Key = VK_RETURN then
469       OpenButtonClick(Nil)
470     else if Key = VK_DELETE then
471       RemoveBitBtnClick(Nil)
472     else if Key = VK_INSERT then
473       AddMenuItemClick(Nil)
474     else
475       Handled := False;
476   finally
477     if Handled then
478       Key := VK_UNKNOWN;
479   end;
480 end;
481 
482 procedure TProjectInspectorForm.ItemsTreeViewSelectionChanged(Sender: TObject);
483 begin
484   UpdateProperties;
485   UpdateButtons;
486 end;
487 
488 procedure TProjectInspectorForm.mnuAddDiskFileClick(Sender: TObject);
489 var
490   OpenDialog: TOpenDialog;
491   ADirectory, NewFilename: String;
492   i: Integer;
493 begin
494   OpenDialog:=IDEOpenDialogClass.Create(nil);
495   try
496     InputHistories.ApplyFileDialogSettings(OpenDialog);
497     ADirectory:=LazProject.Directory;
498     if not FilenameIsAbsolute(ADirectory) then
499       ADirectory:='';
500     if ADirectory<>'' then
501       OpenDialog.InitialDir:=ADirectory;
502     OpenDialog.Title:=lisOpenFile;
503     OpenDialog.Options:=OpenDialog.Options
504                           +[ofFileMustExist,ofPathMustExist,ofAllowMultiSelect];
505     OpenDialog.Filter:=dlgFilterAll+' ('+GetAllFilesMask+')|'+GetAllFilesMask
506                  +'|'+dlgFilterLazarusUnit+' (*.pas;*.pp)|*.pas;*.pp'
507                  +'|'+dlgFilterLazarusInclude+' (*.inc)|*.inc'
508                  +'|'+dlgFilterLazarusForm+' (*.lfm;*.dfm)|*.lfm;*.dfm';
509     if OpenDialog.Execute then
510     begin
511       InputHistories.StoreFileDialogSettings(OpenDialog);
512       for i:=0 to OpenDialog.Files.Count-1 do
513       begin
514         NewFilename := OpenDialog.Files[i];
515         case TPrjFileCheck.AddingFile(LazProject, NewFilename) of
516           mrOk: if not (AddOneFile(NewFilename) in [mrOk, mrIgnore]) then
517                   break;
518           mrIgnore: continue;
519           mrCancel: exit;
520         end;
521       end;
522     end;
523   finally
524     OpenDialog.Free;
525   end;
526 end;
527 
528 procedure TProjectInspectorForm.mnuAddEditorFilesClick(Sender: TObject);
529 begin
530   DoAddMoreDialog;
531 end;
532 
533 procedure TProjectInspectorForm.mnuAddFPMakeReqClick(Sender: TObject);
534 begin
535   DoAddFPMakeDepDialog;
536 end;
537 
538 procedure TProjectInspectorForm.mnuAddReqClick(Sender: TObject);
539 begin
540   DoAddDepDialog;
541 end;
542 
543 procedure TProjectInspectorForm.mnuOpenFolderClick(Sender: TObject);
544 begin
545   OpenDocument(LazProject.Directory);
546 end;
547 
548 procedure TProjectInspectorForm.MoveDependencyUpClick(Sender: TObject);
549 var
550   Dependency: TPkgDependency;
551 begin
552   Dependency:=GetSingleSelectedDependency;
553   if SortAlphabetically or (Dependency=nil) or Dependency.Removed
554   or (Dependency.PrevRequiresDependency=nil) then exit;
555   LazProject.MoveRequiredDependencyUp(Dependency);
556 end;
557 
558 procedure TProjectInspectorForm.MoveDependencyDownClick(Sender: TObject);
559 var
560   Dependency: TPkgDependency;
561 begin
562   Dependency:=GetSingleSelectedDependency;
563   if SortAlphabetically or (Dependency=nil) or Dependency.Removed
564   or (Dependency.NextRequiresDependency=nil) then exit;
565   LazProject.MoveRequiredDependencyDown(Dependency);
566 end;
567 
568 procedure TProjectInspectorForm.SetDependencyDefaultFilenameMenuItemClick(Sender: TObject);
569 begin
570   SetDependencyDefaultFilename(false);
571 end;
572 
573 procedure TProjectInspectorForm.SetDependencyPreferredFilenameMenuItemClick(Sender: TObject);
574 begin
575   SetDependencyDefaultFilename(true);
576 end;
577 
578 procedure TProjectInspectorForm.ClearDependencyFilenameMenuItemClick(Sender: TObject);
579 var
580   CurDependency: TPkgDependency;
581   i: Integer;
582   TVNode: TTreeNode;
583   NodeData: TPENodeData;
584   Item: TObject;
585 begin
586   BeginUpdate;
587   try
588     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
589       TVNode:=ItemsTreeView.Selections[i];
590       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
591       if not (Item is TPkgDependency) then continue;
592       CurDependency:=TPkgDependency(Item);
593       if CurDependency.DefaultFilename='' then exit;
594       CurDependency.DefaultFilename:='';
595       CurDependency.PreferDefaultFilename:=false;
596       LazProject.Modified:=true;
597       UpdateRequiredPackages;
598     end;
599   finally
600     EndUpdate;
601   end;
602 end;
603 
TProjectInspectorForm.AddOneFilenull604 function TProjectInspectorForm.AddOneFile(aFilename: string): TModalResult;
605 var
606   NewUnit: TUnitInfo;
607 begin
608   Result := mrOK;
609   NewUnit:=LazProject.UnitInfoWithFilename(aFilename);
610   if NewUnit<>nil then begin
611     if NewUnit.IsPartOfProject then Exit(mrIgnore);
612   end else begin
613     NewUnit:=TUnitInfo.Create(nil);
614     NewUnit.Filename:=aFilename;
615     if LazProject.AutoCreateForms and FilenameIsPascalUnit(NewUnit.Filename)
616     and (pfMainUnitHasCreateFormStatements in LazProject.Flags) then
617       UpdateUnitInfoResourceBaseClass(NewUnit, true);
618     LazProject.AddFile(NewUnit,false);
619   end;
620   if Assigned(OnAddUnitToProject) then begin
621     Result:=OnAddUnitToProject(Self,NewUnit);
622     if Result<>mrOK then Exit;
623   end;
624   FNextSelectedPart:=NewUnit;
625 end;
626 
627 procedure TProjectInspectorForm.AddMenuItemClick(Sender: TObject);
628 begin
629   //check the selected item in ItemsTreeView
630   // -> if it's "Required Packages", call "New Requirement" (mnuAddReqClick)
631   // -> otherwise (selected = "Files") call "Add files from file system" (AddBitBtnClick)
632   if NodeTreeIsIn(ItemsTreeView.Selected, FDependenciesNode) then
633     mnuAddReqClick(Sender)
634   else
635     mnuAddDiskFileClick(Sender);
636 end;
637 
638 procedure TProjectInspectorForm.DoAddMoreDialog;
639 var
640   Files: TStringList;
641   i: Integer;
642 begin
643   Files:=TStringList.Create;
644   try
645     if ShowAddToProjectDlg(LazProject,Files)<>mrOk then
646       exit;
647     BeginUpdate;
648     for i:=0 to Files.Count-1 do
649       if not (AddOneFile(Files[i]) in [mrOk, mrIgnore]) then break;
650     UpdateAll;
651     EndUpdate;
652   finally
653     Files.Free;
654   end;
655 end;
656 
657 procedure TProjectInspectorForm.DoAddDepDialog;
658 var
659   Deps: TPkgDependencyList;
660   i: Integer;
661   Resu: TModalResult;
662 begin
663   Resu:=ShowAddPkgDependencyDlg(LazProject, Deps);
664   try
665     if (Resu<>mrOK) or (Deps.Count=0) or (OnAddDependency=nil) then exit;
666     try
667       BeginUpdate;
668       for i := 0 to Deps.Count-1 do
669         OnAddDependency(Self, Deps[i]);
670       FNextSelectedPart:=Deps[Deps.Count-1];
671       UpdateRequiredPackages;
672     finally
673       EndUpdate;
674     end;
675   finally
676     Deps.Free;
677   end;
678 end;
679 
680 procedure TProjectInspectorForm.DoAddFPMakeDepDialog;
681 var
682   Deps: TPkgDependencyList;
683   i: Integer;
684   Resu: TModalResult;
685 begin
686   Resu:=ShowAddFPMakeDependencyDlg(LazProject, Deps);
687   try
688     if (Resu<>mrOK) or (Deps.Count=0) then exit;
689     try
690       BeginUpdate;
691       for i := 0 to Deps.Count-1 do
692         OnAddDependency(Self, Deps[i]);
693       FNextSelectedPart:=Deps[Deps.Count-1];
694     finally
695       EndUpdate;
696     end;
697   finally
698     Deps.Free;
699   end;
700 end;
701 
702 procedure TProjectInspectorForm.CopyMoveToDirMenuItemClick(Sender: TObject);
703 begin
704   OnCopyMoveFiles(Self);
705 end;
706 
707 procedure TProjectInspectorForm.DirectoryHierarchyButtonClick(Sender: TObject);
708 begin
709   ShowDirectoryHierarchy:=DirectoryHierarchyButton.Down;
710 end;
711 
712 procedure TProjectInspectorForm.FilterEditKeyDown(Sender: TObject;
713   var Key: Word; Shift: TShiftState);
714 begin
715   if Key = VK_RETURN then
716   begin
717     OpenButtonClick(Nil);
718     Key := VK_UNKNOWN;
719   end;
720 end;
721 
722 procedure TProjectInspectorForm.FormCreate(Sender: TObject);
723 begin
724   if LazarusIDE.IDEStarted and (LazProject=nil) then
725   begin   // User opens this window for the very first time. Set active project.
726     LazProject := Project1;
727     UpdateAll;
728   end;
729   if OPMInterface <> nil then
730     OPMInterface.AddPackageListNotification(@PackageListAvailable);
731 end;
732 
733 procedure TProjectInspectorForm.FormDestroy(Sender: TObject);
734 begin
735   if OPMInterface <> nil then
736     OPMInterface.RemovePackageListNotification(@PackageListAvailable);
737 end;
738 
739 procedure TProjectInspectorForm.FormActivate(Sender: TObject);
740 begin
741   //DebugLn('* TProjectInspectorForm.FormActivate *');
742   ItemsTreeView.OnSelectionChanged := @ItemsTreeViewSelectionChanged;
743 end;
744 
745 procedure TProjectInspectorForm.FormDeactivate(Sender: TObject);
746 begin
747   // Prevent calling handler when the tree gets updated with a project.
748   ItemsTreeView.OnSelectionChanged := Nil;
749 end;
750 
751 procedure TProjectInspectorForm.FormDropFiles(Sender: TObject;
752   const FileNames: array of String);
753 var
754   i: Integer;
755 begin
756   {$IFDEF VerboseProjInspDrag}
757   debugln(['TProjectInspectorForm.FormDropFiles ',length(FileNames)]);
758   {$ENDIF}
759   if length(FileNames)=0 then exit;
760   BeginUpdate;
761   try
762     for i:=0 to high(Filenames) do
763       if not (AddOneFile(FileNames[i]) in [mrOk, mrIgnore]) then break;
764     UpdateAll;
765   finally
766     EndUpdate;
767   end;
768 end;
769 
770 procedure TProjectInspectorForm.ItemsPopupMenuPopup(Sender: TObject);
771 var
772   ItemCnt: integer;
773 
774   function AddPopupMenuItem(const ACaption: string; AnEvent: TNotifyEvent;
775     EnabledFlag: boolean = True): TMenuItem;
776   begin
777     if ItemsPopupMenu.Items.Count<=ItemCnt then begin
778       Result:=TMenuItem.Create(Self);
779       ItemsPopupMenu.Items.Add(Result);
780     end else
781       Result:=ItemsPopupMenu.Items[ItemCnt];
782     Result.Caption:=ACaption;
783     Result.OnClick:=AnEvent;
784     Result.Enabled:=EnabledFlag;
785     Result.Checked:=false;
786     Result.ShowAlwaysCheckable:=false;
787     Result.Visible:=true;
788     Result.RadioItem:=false;
789     Result.ImageIndex:=-1;
790     inc(ItemCnt);
791   end;
792 
793 var
794   i: Integer;
795   TVNode: TTreeNode;
796   NodeData: TPENodeData;
797   Item: TObject;
798   CanRemoveCount: Integer;
799   CanOpenCount: Integer;
800   HasLFMCount: Integer;
801   CurUnitInfo: TUnitInfo;
802   DisabledI18NForLFMCount: Integer;
803   CanReAddCount: Integer;
804   SingleSelectedDep: TPkgDependency;
805   DepCount: Integer;
806   Dependency: TPkgDependency;
807   HasValidDep: Integer;
808   CanClearDep: Integer;
809   CanMoveFileCount: Integer;
810   OpenItemCapt: String;
811 begin
812   ItemCnt:=0;
813 
814   CanRemoveCount:=0;
815   CanOpenCount:=0;
816   CanMoveFileCount:=0;
817   HasLFMCount:=0;
818   DisabledI18NForLFMCount:=0;
819   CanReAddCount:=0;
820   SingleSelectedDep:=nil;
821   DepCount:=0;
822   HasValidDep:=0;
823   CanClearDep:=0;
824   for i:=0 to ItemsTreeView.SelectionCount-1 do begin
825     TVNode:=ItemsTreeView.Selections[i];
826     if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
827     if Item is TUnitInfo then begin
828       CurUnitInfo:=TUnitInfo(Item);
829       inc(CanOpenCount);
830       if (CurUnitInfo<>LazProject.MainUnitInfo) and (not NodeData.Removed) then begin
831         inc(CanRemoveCount);
832         inc(CanMoveFileCount);
833       end;
834       if FilenameIsPascalSource(CurUnitInfo.Filename)
835       and FileExistsCached(ChangeFileExt(CurUnitInfo.Filename,'.lfm')) then begin
836         inc(HasLFMCount);
837         if CurUnitInfo.DisableI18NForLFM then
838           inc(DisabledI18NForLFMCount);
839       end;
840     end else if Item is TPkgDependency then begin
841       Dependency:=TPkgDependency(Item);
842       if NodeData.Removed then begin
843         inc(CanReAddCount);
844       end else begin
845         inc(DepCount);
846         if DepCount=1 then
847           SingleSelectedDep:=Dependency
848         else
849           SingleSelectedDep:=nil;
850         inc(CanRemoveCount);
851         if Dependency.DependencyType=pdtLazarus then
852           inc(CanOpenCount);
853         if Dependency.RequiredPackage<>nil then
854           inc(HasValidDep);
855         if (Dependency.DefaultFilename<>'') then
856           inc(CanClearDep);
857       end;
858     end;
859   end;
860 
861   if ItemsTreeView.Selected = FFilesNode then
862   begin
863     // Only the Files node is selected.
864     Assert(AddBitBtn.Enabled, 'AddBitBtn not Enabled');
865     AddPopupMenuItem(lisBtnDlgAdd, @mnuAddDiskFileClick);
866     if not LazProject.IsVirtual then
867       AddPopupMenuItem(lisRemoveNonExistingFiles,@RemoveNonExistingFilesMenuItemClick);
868     AddPopupMenuItem(cLineCaption, Nil, False);                // Separator
869     AddPopupMenuItem(lisMenuOpenFolder, @mnuOpenFolderClick);
870   end
871   else if ItemsTreeView.Selected = FDependenciesNode then
872   begin
873     // Only the Required Packages node is selected.
874     AddPopupMenuItem(lisBtnDlgAdd, @mnuAddReqClick);
875   end
876   else begin
877     // Files, dependencies or everything mixed is selected.
878     if CanOpenCount>0 then begin
879       OpenItemCapt := lisOpen;
880       if Assigned(SingleSelectedDep) then
881         case SingleSelectedDep.LoadPackageResult of
882           lprAvailableOnline:
883             OpenItemCapt:=lisPckEditInstall;
884           lprNotFound:
885             if Assigned(OPMInterface) and not OPMInterface.IsPackageListLoaded then
886               OpenItemCapt:=lisPckEditCheckAvailabilityOnline;
887         end;
888       AddPopupMenuItem(OpenItemCapt, @OpenButtonClick);
889     end;
890     if CanRemoveCount>0 then
891       AddPopupMenuItem(lisRemove, @RemoveBitBtnClick);
892     // files section
893     if CanMoveFileCount>0 then
894       AddPopupMenuItem(lisCopyMoveFileToDirectory,@CopyMoveToDirMenuItemClick);
895   end;
896 
897   if LazProject.EnableI18N and LazProject.EnableI18NForLFM
898   and (HasLFMCount>0) then begin
899     AddPopupMenuItem(lisEnableI18NForLFM,
900       @EnableI18NForLFMMenuItemClick, DisabledI18NForLFMCount>0);
901     AddPopupMenuItem(lisDisableI18NForLFM,
902       @DisableI18NForLFMMenuItemClick, DisabledI18NForLFMCount<HasLFMCount);
903   end;
904 
905   // Required packages section
906   if CanReAddCount>0 then
907     AddPopupMenuItem(lisPckEditReAddDependency, @ReAddMenuItemClick, true);
908   if SingleSelectedDep<>nil then begin
909     AddPopupMenuItem(lisPckEditMoveDependencyUp, @MoveDependencyUpClick,
910                      (SingleSelectedDep.PrevRequiresDependency<>nil));
911     AddPopupMenuItem(lisPckEditMoveDependencyDown, @MoveDependencyDownClick,
912                      (SingleSelectedDep.NextRequiresDependency<>nil));
913   end;
914   if HasValidDep>0 then begin
915     AddPopupMenuItem(lisPckEditStoreFileNameAsDefaultForThisDependency,
916                      @SetDependencyDefaultFilenameMenuItemClick, true);
917     AddPopupMenuItem(lisPckEditStoreFileNameAsPreferredForThisDependency,
918                      @SetDependencyPreferredFilenameMenuItemClick, true);
919   end;
920   if CanClearDep>0 then begin
921     AddPopupMenuItem(lisPckEditClearDefaultPreferredFilenameOfDependency,
922                      @ClearDependencyFilenameMenuItemClick, true);
923   end;
924 
925   while ItemsPopupMenu.Items.Count>ItemCnt do
926     ItemsPopupMenu.Items.Delete(ItemsPopupMenu.Items.Count-1);
927 end;
928 
929 procedure TProjectInspectorForm.ItemsTreeViewAdvancedCustomDrawItem(
930   Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
931   Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
932 var
933   NodeData: TPENodeData;
934   r: TRect;
935   y: Integer;
936 begin
937   if Stage=cdPostPaint then begin
938     NodeData:=GetNodeData(Node);
939     if (NodeData<>nil) then begin
940       if  (NodeData.Typ=penFile) and (not NodeData.Removed)
941       and FilenameIsAbsolute(NodeData.Name)
942       and (not FileExistsCached(NodeData.Name))
943       then begin
944         r:=Node.DisplayRect(true);
945         ItemsTreeView.Canvas.Pen.Color:=clRed;
946         y:=(r.Top+r.Bottom) div 2;
947         ItemsTreeView.Canvas.Line(r.Left,y,r.Right,y);
948       end;
949     end;
950   end;
951 end;
952 
953 procedure TProjectInspectorForm.OpenButtonClick(Sender: TObject);
954 var
955   i: Integer;
956   NodeData: TPENodeData;
957   Item: TObject;
958 begin
959   for i:=0 to ItemsTreeView.SelectionCount-1 do
960   begin
961     if GetNodeDataItem(ItemsTreeView.Selections[i], NodeData, Item) then
962     begin
963       if Item is TUnitInfo then
964       begin
965         if LazarusIDE.DoOpenEditorFile(TUnitInfo(Item).Filename,
966                                        -1,-1,[ofAddToRecent]) <> mrOk then
967           Exit;
968       end
969       else if Item is TPkgDependency then
970         if not OpmAddOrOpenDependency(TPkgDependency(Item)) then
971           Exit;
972     end;
973   end;
974   OpmInstallPendingDependencies;
975 end;
976 
977 procedure TProjectInspectorForm.OptionsBitBtnClick(Sender: TObject);
978 begin
979   if Assigned(OnShowOptions) then OnShowOptions(Self);
980 end;
981 
982 procedure TProjectInspectorForm.HelpBitBtnClick(Sender: TObject);
983 begin
984   LazarusHelp.ShowHelpForIDEControl(Self);
985 end;
986 
987 procedure TProjectInspectorForm.ReAddMenuItemClick(Sender: TObject);
988 var
989   Dependency: TPkgDependency;
990   i: Integer;
991   TVNode: TTreeNode;
992   NodeData: TPENodeData;
993   Item: TObject;
994 begin
995   BeginUpdate;
996   try
997     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
998       TVNode:=ItemsTreeView.Selections[i];
999       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1000       if not NodeData.Removed then continue;
1001       if not (Item is TPkgDependency) then continue;
1002       Dependency:=TPkgDependency(Item);
1003       if TPrjFileCheck.AddingDependency(LazProject,Dependency)<>mrOK then exit;
1004       if Assigned(OnReAddDependency) then
1005         OnReAddDependency(Self,Dependency);
1006     end;
1007   finally
1008     EndUpdate;
1009   end;
1010 end;
1011 
1012 procedure TProjectInspectorForm.RemoveBitBtnClick(Sender: TObject);
1013 var
1014   CurDependency: TPkgDependency;
1015   i: Integer;
1016   TVNode: TTreeNode;
1017   NodeData: TPENodeData;
1018   Item: TObject;
1019   Msg, Cap: String;
1020   DeleteCount: Integer;
1021   CurFile: TUnitInfo;
1022 begin
1023   BeginUpdate;
1024   try
1025     // check selection
1026     Msg:='';
1027     DeleteCount:=0;
1028     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1029       TVNode:=ItemsTreeView.Selections[i];
1030       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1031       if Item is TUnitInfo then begin
1032         CurFile:=TUnitInfo(Item);
1033         if CurFile=LazProject.MainUnitInfo then continue;
1034         // remove file
1035         inc(DeleteCount);
1036         Msg:=Format(lisProjInspRemoveFileFromProject, [CurFile.Filename]);
1037       end else if Item is TPkgDependency then begin
1038         CurDependency:=TPkgDependency(item);
1039         if NodeData.Removed then continue;
1040         // remove dependency
1041         inc(DeleteCount);
1042         Msg:=Format(lisProjInspDeleteDependencyFor, [CurDependency.AsString]);
1043       end;
1044     end;
1045 
1046     // ask for confirmation
1047     if DeleteCount=0 then exit;
1048     if DeleteCount>1 then
1049       Msg:=Format(lisProjInspRemoveItemsF, [IntToStr(DeleteCount)]);
1050     if CurFile<>nil then
1051       Cap:=lisProjInspConfirmRemovingFile
1052     else
1053       Cap:=lisProjInspConfirmDeletingDependency;
1054     if IDEMessageDialog(Cap,
1055       Msg, mtConfirmation,[mbYes,mbNo])<>mrYes then exit;
1056 
1057     // delete
1058     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1059       TVNode:=ItemsTreeView.Selections[i];
1060       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1061       if Item is TUnitInfo then begin
1062         CurFile:=TUnitInfo(Item);
1063         if CurFile=LazProject.MainUnitInfo then continue;
1064         // remove file
1065         if Assigned(OnRemoveFile) then OnRemoveFile(Self,CurFile);
1066       end else if Item is TPkgDependency then begin
1067         CurDependency:=TPkgDependency(item);
1068         if NodeData.Removed then continue;
1069         // remove dependency
1070         if Assigned(OnRemoveDependency) then
1071           OnRemoveDependency(Self,CurDependency);
1072       end;
1073     end;
1074   finally
1075     EndUpdate;
1076   end;
1077 end;
1078 
1079 procedure TProjectInspectorForm.RemoveNonExistingFilesMenuItemClick(Sender: TObject);
1080 var
1081   AnUnitInfo: TUnitInfo;
1082   NextUnitInfo: TUnitInfo;
1083   HasChanged: Boolean;
1084 begin
1085   if LazProject.IsVirtual then exit;
1086   BeginUpdate;
1087   try
1088     HasChanged:=false;
1089     AnUnitInfo:=LazProject.FirstPartOfProject;
1090     while AnUnitInfo<>nil do begin
1091       NextUnitInfo:=AnUnitInfo.NextPartOfProject;
1092       if not (AnUnitInfo.IsVirtual or FileExistsUTF8(AnUnitInfo.Filename)) then begin
1093         AnUnitInfo.IsPartOfProject:=false;
1094         HasChanged:=true;
1095       end;
1096       AnUnitInfo:=NextUnitInfo;
1097     end;
1098     if HasChanged then begin
1099       LazProject.Modified:=true;
1100       UpdateFiles;
1101     end;
1102   finally
1103     EndUpdate;
1104   end;
1105 end;
1106 
1107 procedure TProjectInspectorForm.SortAlphabeticallyButtonClick(Sender: TObject);
1108 begin
1109   SortAlphabetically:=SortAlphabeticallyButton.Down;
1110 end;
1111 
1112 procedure TProjectInspectorForm.EnableI18NForLFMMenuItemClick(Sender: TObject);
1113 begin
1114   EnableI18NForSelectedLFM(true);
1115 end;
1116 
1117 procedure TProjectInspectorForm.DisableI18NForLFMMenuItemClick(Sender: TObject);
1118 begin
1119   EnableI18NForSelectedLFM(false);
1120 end;
1121 
1122 procedure TProjectInspectorForm.SetLazProject(const AValue: TProject);
1123 begin
1124   if FLazProject=AValue then exit;
1125   if FLazProject<>nil then begin       // Old Project
1126     dec(FUpdateLock,LazProject.UpdateLock);
1127     FLazProject.OnBeginUpdate:=nil;
1128     FLazProject.OnEndUpdate:=nil;
1129   end;
1130   FLazProject:=AValue;
1131   if FLazProject<>nil then begin       // New Project
1132     inc(FUpdateLock,LazProject.UpdateLock);
1133     FLazProject.OnBeginUpdate:=@ProjectBeginUpdate;
1134     FLazProject.OnEndUpdate:=@ProjectEndUpdate;
1135   end
1136   else // Only update when no project. ProjectEndUpdate will update a project.
1137     UpdateAll;
1138 end;
1139 
1140 procedure TProjectInspectorForm.SetShowDirectoryHierarchy(const AValue: boolean);
1141 begin
1142   if FShowDirectoryHierarchy=AValue then exit;
1143   FShowDirectoryHierarchy:=AValue;
1144   DirectoryHierarchyButton.Down:=FShowDirectoryHierarchy;
1145   FilterEdit.ShowDirHierarchy:=FShowDirectoryHierarchy;
1146   FilterEdit.InvalidateFilter;
1147   EnvironmentOptions.ProjInspShowDirHierarchy := ShowDirectoryHierarchy;
1148 end;
1149 
1150 procedure TProjectInspectorForm.SetSortAlphabetically(const AValue: boolean);
1151 begin
1152   if FSortAlphabetically=AValue then exit;
1153   FSortAlphabetically:=AValue;
1154   SortAlphabeticallyButton.Down:=FSortAlphabetically;
1155   FilterEdit.SortData:=FSortAlphabetically;
1156   FilterEdit.InvalidateFilter;
1157   EnvironmentOptions.ProjInspSortAlphabetically := SortAlphabetically;
1158 end;
1159 
1160 procedure TProjectInspectorForm.SetDependencyDefaultFilename(AsPreferred: boolean);
1161 var
1162   NewFilename: String;
1163   CurDependency: TPkgDependency;
1164   i: Integer;
1165   TVNode: TTreeNode;
1166   NodeData: TPENodeData;
1167   Item: TObject;
1168 begin
1169   BeginUpdate;
1170   try
1171     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1172       TVNode:=ItemsTreeView.Selections[i];
1173       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1174       if NodeData.Removed then continue;
1175       if not (Item is TPkgDependency) then continue;
1176       CurDependency:=TPkgDependency(Item);
1177       if CurDependency.RequiredPackage=nil then continue;
1178       NewFilename:=CurDependency.RequiredPackage.Filename;
1179       if (NewFilename=CurDependency.DefaultFilename) // do not use CompareFilenames
1180       and (CurDependency.PreferDefaultFilename=AsPreferred) then continue;
1181       CurDependency.DefaultFilename:=NewFilename;
1182       CurDependency.PreferDefaultFilename:=AsPreferred;
1183       LazProject.Modified:=true;
1184       UpdateRequiredPackages;
1185     end;
1186   finally
1187     EndUpdate;
1188   end;
1189 end;
1190 
1191 procedure TProjectInspectorForm.SetIdleConnected(AValue: boolean);
1192 begin
1193   if csDestroying in ComponentState then
1194     AValue:=false;
1195   if FIdleConnected=AValue then exit;
1196   FIdleConnected:=AValue;
1197   if FIdleConnected then
1198     Application.AddOnIdleHandler(@IdleHandler)
1199   else
1200     Application.RemoveOnIdleHandler(@IdleHandler);
1201 end;
1202 
GetDependencyToUpdatenull1203 function TProjectInspectorForm.GetDependencyToUpdate(Immediately: boolean): TPkgDependencyID;
1204 begin
1205   if CanUpdate(pefNeedUpdateApplyDependencyButton,Immediately) then
1206     Result:=GetSingleSelectedDependency
1207   else
1208     Result:=nil;
1209 end;
1210 
1211 procedure TProjectInspectorForm.ApplyDependencyButtonClick(Sender: TObject);
1212 var
1213   CurDependency: TPkgDependency;
1214 begin
1215   CurDependency:=GetSingleSelectedDependency;
1216   if (LazProject=nil) or (CurDependency=nil)
1217   or not FPropGui.CheckApplyDependency(CurDependency) then exit;
1218   LazProject.Modified:=True;
1219   PkgBoss.ApplyDependency(CurDependency);
1220 end;
1221 
CreateToolButtonnull1222 function TProjectInspectorForm.CreateToolButton(AName, ACaption, AHint, AImageName: String;
1223   AOnClick: TNotifyEvent): TToolButton;
1224 begin
1225   Result := TToolButton.Create(Self);
1226   Result.Name := AName;
1227   Result.Caption := ACaption;
1228   Result.Hint := AHint;
1229   if AImageName <> '' then
1230     Result.ImageIndex := IDEImages.LoadImage(AImageName);
1231   Result.ShowHint := True;
1232   Result.OnClick := AOnClick;
1233   Result.AutoSize := True;
1234   Result.Parent := ToolBar;
1235 end;
1236 
CreateDividernull1237 function TProjectInspectorForm.CreateDivider: TToolButton;
1238 begin
1239   Result := TToolButton.Create(Self);
1240   Result.Style := tbsDivider;
1241   Result.AutoSize := True;
1242   Result.Parent := ToolBar;
1243 end;
1244 
1245 procedure TProjectInspectorForm.SetupComponents;
1246 begin
1247   ImageIndexFiles           := IDEImages.LoadImage('pkg_files');
1248   ImageIndexProject         := IDEImages.LoadImage('item_project_source');
1249   ImageIndexUnit            := IDEImages.LoadImage('item_unit');
1250   ImageIndexRegisterUnit    := IDEImages.LoadImage('pkg_registerunit');
1251   ImageIndexText            := IDEImages.LoadImage('pkg_text');
1252   ImageIndexBinary          := IDEImages.LoadImage('pkg_binary');
1253   ImageIndexDirectory       := IDEImages.LoadImage('pkg_files');
1254 
1255   ItemsTreeView.Images      := IDEImages.Images_16;
1256   ToolBar.Images            := IDEImages.Images_16;
1257   FilterEdit.OnGetImageIndex:=@TreeViewGetImageIndex;
1258 
1259   AddBitBtn     := CreateToolButton('AddBitBtn', lisAdd, lisClickToSeeTheChoices, 'laz_add', nil);
1260   AddBitBtn.Style:=tbsButtonDrop;
1261   RemoveBitBtn  := CreateToolButton('RemoveBitBtn', lisRemove, lisPckEditRemoveSelectedItem, 'laz_delete', @RemoveBitBtnClick);
1262   CreateDivider;
1263   OptionsBitBtn := CreateToolButton('OptionsBitBtn', lisOptions, lisPckEditEditGeneralOptions, 'menu_environment_options', @OptionsBitBtnClick);
1264   OptionsBitBtn.DropdownMenu := TSetBuildModeToolButton.TBuildModeMenu.Create(Self);
1265   OptionsBitBtn.Style := tbsDropDown;
1266   HelpBitBtn    := CreateToolButton('HelpBitBtn', GetButtonCaption(idButtonHelp), lisMenuOnlineHelp, 'btn_help', @HelpBitBtnClick);
1267 
1268   AddBitBtn.DropdownMenu:=AddPopupMenu;
1269   mnuAddDiskFile.Caption:=lisPckEditAddFilesFromFileSystem;
1270   mnuAddEditorFiles.Caption:=lisProjAddEditorFile;
1271   mnuAddReq.Caption:=lisProjAddNewRequirement;
1272   mnuAddFPMakeReq.Caption:=lisProjAddNewFPMakeRequirement;
1273 
1274   IDEImages.AssignImage(OpenButton, 'laz_open');
1275   OpenButton.Caption:='';
1276   OpenButton.Hint:=lisOpenFile2;
1277   SortAlphabeticallyButton.Hint:=lisPESortFilesAlphabetically;
1278   IDEImages.AssignImage(SortAlphabeticallyButton, 'pkg_sortalphabetically');
1279   DirectoryHierarchyButton.Hint:=lisPEShowDirectoryHierarchy;
1280   IDEImages.AssignImage(DirectoryHierarchyButton, 'pkg_hierarchical');
1281 
1282   FPropGui.OnGetPkgDep := @GetDependencyToUpdate;
1283   FPropGui.ApplyDependencyButton.OnClick := @ApplyDependencyButtonClick;
1284 
1285   with ItemsTreeView do begin
1286     FFilesNode:=Items.Add(nil, dlgEnvFiles);
1287     FFilesNode.ImageIndex:=ImageIndexFiles;
1288     FFilesNode.SelectedIndex:=FFilesNode.ImageIndex;
1289     FDependenciesNode:=Items.Add(nil, lisPckEditRequiredPackages);
1290     FDependenciesNode.ImageIndex:=FPropGui.ImageIndexRequired;
1291     FDependenciesNode.SelectedIndex:=FDependenciesNode.ImageIndex;
1292   end;
1293 end;
1294 
TreeViewGetImageIndexnull1295 function TProjectInspectorForm.TreeViewGetImageIndex(Str: String; Data: TObject;
1296                                                 var AIsEnabled: Boolean): Integer;
1297 var
1298   NodeData: TPENodeData;
1299   Item: TObject;
1300 begin
1301   Result := -1;
1302   if not (Data is TPENodeData) then exit;
1303   NodeData:=TPENodeData(Data);
1304   Item:=GetNodeItem(NodeData);
1305   if Item=nil then exit;
1306   if Item is TUnitInfo then begin
1307     if FilenameHasPascalExt(TUnitInfo(Item).Filename) then
1308       Result:=ImageIndexUnit
1309     else if (LazProject<>nil) and (LazProject.MainUnitinfo=Item) then
1310       Result:=ImageIndexProject
1311     else
1312       Result:=ImageIndexText;
1313   end
1314   else if Item is TPkgDependency then
1315     Result:=FPropGui.GetDependencyImageIndex(TPkgDependency(Item));
1316 end;
1317 
1318 procedure TProjectInspectorForm.UpdateFiles(Immediately: boolean);
1319 var
1320   CurFile: TUnitInfo;
1321   FilesBranch: TTreeFilterBranch;
1322   Filename: String;
1323   ANodeData : TPENodeData;
1324 begin
1325   if not CanUpdate(pefNeedUpdateFiles,Immediately) then exit;
1326   FilesBranch:=FilterEdit.GetCleanBranch(FFilesNode);
1327   FilesBranch.ClearNodeData;
1328   FPropGui.FreeNodeData(penFile);
1329   if LazProject<>nil then begin
1330     FilterEdit.SelectedPart:=FNextSelectedPart;
1331     FilterEdit.ShowDirHierarchy:=ShowDirectoryHierarchy;
1332     FilterEdit.SortData:=SortAlphabetically;
1333     FilterEdit.ImageIndexDirectory:=ImageIndexDirectory;
1334     // collect and sort files
1335     CurFile:=LazProject.FirstPartOfProject;
1336     while CurFile<>nil do begin
1337       Filename:=CurFile.GetShortFilename(true);
1338       if Filename<>'' then Begin
1339         ANodeData:=FPropGui.CreateNodeData(penFile, CurFile.Filename, False);
1340         FilesBranch.AddNodeData(Filename, ANodeData, CurFile.Filename);
1341       end;
1342       CurFile:=CurFile.NextPartOfProject;
1343     end;
1344   end;
1345   FilterEdit.InvalidateFilter;            // Data is shown by FilterEdit.
1346   UpdateProperties;
1347   UpdateButtons;
1348 end;
1349 
1350 procedure TProjectInspectorForm.UpdateRequiredPackages(Immediately: boolean);
1351 var
1352   Dependency: TPkgDependency;
1353   RequiredBranch, RemovedBranch: TTreeFilterBranch;
1354   ANodeData : TPENodeData;
1355 begin
1356   if not CanUpdate(pefNeedUpdateRequiredPkgs,Immediately) then exit;
1357   RequiredBranch:=FilterEdit.GetCleanBranch(FDependenciesNode);
1358   RequiredBranch.ClearNodeData;
1359   FPropGui.FreeNodeData(penDependency);
1360   Dependency:=Nil;
1361   if LazProject<>nil then begin
1362     // required packages
1363     Dependency:=LazProject.FirstRequiredDependency;
1364     while Dependency<>nil do begin
1365       // Add the required package under the branch
1366       ANodeData:=FPropGui.CreateNodeData(penDependency, Dependency.PackageName, False);
1367       RequiredBranch.AddNodeData(Dependency.AsString(False,True)+OPNote(Dependency), ANodeData);
1368       Dependency:=Dependency.NextRequiresDependency;
1369     end;
1370 
1371     // removed required packages
1372     Dependency:=LazProject.FirstRemovedDependency;
1373     if Dependency<>nil then begin
1374       // Create root node for removed dependencies if not done yet.
1375       if FRemovedDependenciesNode=nil then begin
1376         FRemovedDependenciesNode:=ItemsTreeView.Items.Add(FDependenciesNode,
1377                                                 lisProjInspRemovedRequiredPackages);
1378         FRemovedDependenciesNode.ImageIndex:=FPropGui.ImageIndexRemovedRequired;
1379         FRemovedDependenciesNode.SelectedIndex:=FRemovedDependenciesNode.ImageIndex;
1380       end;
1381       RemovedBranch:=FilterEdit.GetCleanBranch(FRemovedDependenciesNode);
1382       // Add all removed dependencies under the branch
1383       while Dependency<>nil do begin
1384         ANodeData := FPropGui.CreateNodeData(penDependency, Dependency.PackageName, True);
1385         RemovedBranch.AddNodeData(Dependency.AsString, ANodeData);
1386         Dependency:=Dependency.NextRequiresDependency;
1387       end;
1388     end;
1389   end;
1390 
1391   // Dependency is set to removed required packages if there is active project
1392   if (Dependency=nil) and (FRemovedDependenciesNode<>nil) then begin
1393     // No removed dependencies -> delete the root node
1394     FilterEdit.DeleteBranch(FRemovedDependenciesNode);
1395     FreeThenNil(FRemovedDependenciesNode);
1396   end;
1397   FilterEdit.InvalidateFilter;
1398   UpdateProperties;
1399   UpdateButtons;
1400 end;
1401 
1402 procedure TProjectInspectorForm.ProjectBeginUpdate(Sender: TObject);
1403 begin
1404   BeginUpdate;
1405 end;
1406 
1407 procedure TProjectInspectorForm.ProjectEndUpdate(Sender: TObject; ProjectChanged: boolean);
1408 begin
1409   if ProjectChanged then
1410     UpdateAll;
1411   EndUpdate;
1412 end;
1413 
1414 procedure TProjectInspectorForm.EnableI18NForSelectedLFM(TheEnable: boolean);
1415 var
1416   i: Integer;
1417   TVNode: TTreeNode;
1418   NodeData: TPENodeData;
1419   Item: TObject;
1420   CurUnitInfo: TUnitInfo;
1421 begin
1422   for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1423     TVNode:=ItemsTreeView.Selections[i];
1424     if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1425     if not (Item is TUnitInfo) then continue;
1426     CurUnitInfo:=TUnitInfo(Item);
1427     if not FilenameIsPascalSource(CurUnitInfo.Filename) then continue;
1428     CurUnitInfo.DisableI18NForLFM:=not TheEnable;
1429   end;
1430 end;
1431 
1432 procedure TProjectInspectorForm.PackageListAvailable(Sender: TObject);
1433 begin
1434   //DebugLn(['TProjectInspectorForm.PackageListAvailable: ', LazProject.Title]);
1435   UpdateRequiredPackages;
1436 end;
1437 
1438 procedure TProjectInspectorForm.KeyDown(var Key: Word; Shift: TShiftState);
1439 begin
1440   inherited KeyDown(Key, Shift);
1441   ExecuteIDEShortCut(Self,Key,Shift,nil);
1442 end;
1443 
1444 procedure TProjectInspectorForm.IdleHandler(Sender: TObject; var Done: Boolean);
1445 begin
1446   if fUpdateLock>0 then exit;
1447   IdleConnected:=false;
1448   UpdatePending;
1449 end;
1450 
GetSingleSelectedDependencynull1451 function TProjectInspectorForm.GetSingleSelectedDependency: TPkgDependency;
1452 var
1453   Item: TObject;
1454   NodeData: TPENodeData;
1455 begin
1456   Result:=nil;
1457   if not GetNodeDataItem(ItemsTreeView.Selected,NodeData,Item) then exit;
1458   if Item is TPkgDependency then
1459     Result:=TPkgDependency(Item);
1460 end;
1461 
TreeViewToInspectornull1462 function TProjectInspectorForm.TreeViewToInspector(TV: TTreeView): TProjectInspectorForm;
1463 begin
1464   if TV=ItemsTreeView then
1465     Result:=Self
1466   else
1467     Result:=nil;
1468 end;
1469 
1470 constructor TProjectInspectorForm.Create(TheOwner: TComponent);
1471 begin
1472   inherited Create(TheOwner);
1473   Name:=NonModalIDEWindowNames[nmiwProjectInspector];
1474   Caption:=lisMenuProjectInspector;
1475   KeyPreview:=true;
1476   FPropGui:=TProjPackFilePropGui.Create(PropsGroupBox, False);
1477   SetupComponents;
1478   KeyPreview:=true;
1479   SortAlphabetically := EnvironmentOptions.ProjInspSortAlphabetically;
1480   ShowDirectoryHierarchy := EnvironmentOptions.ProjInspShowDirHierarchy;
1481 end;
1482 
1483 destructor TProjectInspectorForm.Destroy;
1484 begin
1485   IdleConnected:=false;
1486   LazProject:=nil;
1487   inherited Destroy;
1488   FreeAndNil(FPropGui);
1489   if ProjInspector=Self then
1490     ProjInspector:=nil;
1491 end;
1492 
ExtendIncSearchPathnull1493 function TProjectInspectorForm.ExtendIncSearchPath(NewIncPaths: string): boolean;
1494 begin
1495   Result:=LazProject.ExtendIncSearchPath(NewIncPaths);
1496 end;
1497 
ExtendUnitSearchPathnull1498 function TProjectInspectorForm.ExtendUnitSearchPath(NewUnitPaths: string): boolean;
1499 begin
1500   Result:=LazProject.ExtendUnitSearchPath(NewUnitPaths);
1501 end;
1502 
FilesBaseDirectorynull1503 function TProjectInspectorForm.FilesBaseDirectory: string;
1504 begin
1505   if LazProject<>nil then
1506     Result:=LazProject.Directory
1507   else
1508     Result:='';
1509 end;
1510 
FilesEditFormnull1511 function TProjectInspectorForm.FilesEditForm: TCustomForm;
1512 begin
1513   Result:=Self;
1514 end;
1515 
FilesEditTreeViewnull1516 function TProjectInspectorForm.FilesEditTreeView: TTreeView;
1517 begin
1518   Result:=ItemsTreeView;
1519 end;
1520 
FilesOwnernull1521 function TProjectInspectorForm.FilesOwner: TObject;
1522 begin
1523   Result:=LazProject;
1524 end;
1525 
FilesOwnerNamenull1526 function TProjectInspectorForm.FilesOwnerName: string;
1527 begin
1528   Result:=lisProject3;
1529 end;
1530 
FilesOwnerReadOnlynull1531 function TProjectInspectorForm.FilesOwnerReadOnly: boolean;
1532 begin
1533   Result:=false;
1534 end;
1535 
FirstRequiredDependencynull1536 function TProjectInspectorForm.FirstRequiredDependency: TPkgDependency;
1537 begin
1538   if LazProject<>nil then
1539     Result:=LazProject.FirstRequiredDependency
1540   else
1541     Result:=nil;
1542 end;
1543 
GetNodeFilenamenull1544 function TProjectInspectorForm.GetNodeFilename(Node: TTreeNode): string;
1545 var
1546   Item: TFileNameItem;
1547 begin
1548   Result:='';
1549   if Node=nil then exit;
1550   if Node=FFilesNode then
1551     exit(FilesBaseDirectory);
1552   Item:=TFileNameItem(Node.Data);
1553   if (Item is TFileNameItem) then begin
1554     Result:=Item.Filename;
1555   end else if Node.HasAsParent(FFilesNode) then begin
1556     // directory node
1557     Result:=Node.Text;
1558   end else
1559     exit;
1560   if not FilenameIsAbsolute(Result) then
1561     Result:=AppendPathDelim(FilesBaseDirectory)+Result;
1562 end;
1563 
IsDirectoryNodenull1564 function TProjectInspectorForm.IsDirectoryNode(Node: TTreeNode): boolean;
1565 begin
1566   Result:=(Node<>nil) and (Node.Data=nil) and Node.HasAsParent(FFilesNode);
1567 end;
1568 
1569 procedure TProjectInspectorForm.BeginUpdate;
1570 begin
1571   inc(FUpdateLock);
1572 end;
1573 
1574 procedure TProjectInspectorForm.EndUpdate;
1575 begin
1576   if FUpdateLock=0 then RaiseGDBException('TProjectInspectorForm.EndUpdate');
1577   dec(FUpdateLock);
1578   if FUpdateLock=0 then
1579     IdleConnected:=true;
1580 end;
1581 
1582 procedure TProjectInspectorForm.UpdateAll(Immediately: boolean);
1583 begin
1584   if csDestroying in ComponentState then exit;
1585   fFlags:=fFlags+[
1586     pefNeedUpdateTitle,
1587     pefNeedUpdateFiles,
1588     pefNeedUpdateRequiredPkgs,
1589     pefNeedUpdateProperties,
1590     pefNeedUpdateButtons,
1591     pefNeedUpdateApplyDependencyButton,
1592     pefNeedUpdateStatusBar];
1593   //UpdateTitle;
1594   //UpdateFiles;
1595   //UpdateRequiredPackages;
1596   //UpdateButtons;
1597   if Immediately then
1598     UpdatePending
1599   else
1600     IdleConnected:=true;
1601 end;
1602 
1603 procedure TProjectInspectorForm.UpdateTitle(Immediately: boolean);
1604 var
1605   NewCaption: String;
1606   IconStream: TStream;
1607 begin
1608   if not CanUpdate(pefNeedUpdateTitle,Immediately) then exit;
1609   Icon.Clear;
1610   if LazProject=nil then
1611     Caption:=lisMenuProjectInspector
1612   else begin
1613     NewCaption:=LazProject.GetTitle;
1614     if NewCaption='' then
1615       NewCaption:=ExtractFilenameOnly(LazProject.ProjectInfoFile);
1616     NewCaption:=Format(lisProjInspProjectInspector, [NewCaption]);
1617     if (LazProject.ActiveBuildMode.GetCaption<>'') then
1618       NewCaption := NewCaption + ' ['+LazProject.ActiveBuildMode.GetCaption+']';
1619     Caption := NewCaption;
1620 
1621     if not LazProject.ProjResources.ProjectIcon.IsEmpty then
1622     begin
1623       IconStream := LazProject.ProjResources.ProjectIcon.GetStream;
1624       if IconStream<>nil then
1625         try
1626           Icon.LoadFromStream(IconStream);
1627         finally
1628           IconStream.Free;
1629         end;
1630     end;
1631   end;
1632 end;
1633 
1634 procedure TProjectInspectorForm.UpdateProperties(Immediately: boolean);
1635 var
1636   CurDependency, SingleSelectedDep: TPkgDependency;
1637   TVNode, SingleSelectedDirectory: TTreeNode;
1638   NodeData: TPENodeData;
1639   Item: TObject;
1640   i, SelFileCount, SelDepCount, SelUnitCount, SelDirCount: Integer;
1641   SingleSelectedRemoved: Boolean;
1642 begin
1643   if not CanUpdate(pefNeedUpdateProperties,Immediately) then exit;
1644   //GuiToFileOptions(False);
1645 
1646   // check selection
1647   SingleSelectedDep:=nil;
1648   SingleSelectedDirectory:=nil;
1649   SingleSelectedRemoved:=false;
1650   SelFileCount:=0;
1651   SelDepCount:=0;
1652   SelUnitCount:=0;
1653   SelDirCount:=0;
1654   for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1655     TVNode:=ItemsTreeView.Selections[i];
1656     if GetNodeDataItem(TVNode,NodeData,Item) then begin
1657       if Item is TUnitInfo then begin
1658         inc(SelFileCount);
1659         SingleSelectedRemoved:=NodeData.Removed;
1660         inc(SelUnitCount);
1661       end
1662       else if Item is TPkgDependency then begin
1663         inc(SelDepCount);
1664         CurDependency:=TPkgDependency(Item);
1665         SingleSelectedDep:=CurDependency;
1666         SingleSelectedRemoved:=NodeData.Removed;
1667       end;
1668     end else if IsDirectoryNode(TVNode) or (TVNode=FFilesNode) then begin
1669       inc(SelDirCount);
1670       SingleSelectedDirectory:=TVNode;
1671     end //else if TVNode=FRequiredPackagesNode then
1672       // DebugLn('UpdatePEProperties: Required packages selected');
1673   end;
1674 
1675   if (SelFileCount+SelDepCount+SelDirCount>1) then begin
1676     // it is a multi selection
1677     SingleSelectedDep:=nil;
1678     SingleSelectedDirectory:=nil;
1679   end;
1680 
1681   //OnlyFilesWithUnitsSelected:=
1682   //  (SelFileCount>0) and (SelDepCount=0) and (SelDirCount=0) and (SelUnitCount>0);
1683   //FPropGui.ControlVisible := OnlyFilesWithUnitsSelected;
1684   FPropGui.ControlEnabled := True; //not LazProject.ReadOnly;
1685   DisableAlign;
1686   try
1687     // Min/Max version of dependency (only single selection)
1688     FPropGui.ControlVisible := SingleSelectedDep<>nil;
1689     FPropGui.SetMinMaxVisibility;
1690     if SelFileCount>0 then begin
1691       PropsGroupBox.Enabled:=true;
1692       PropsGroupBox.Caption:=lisPckEditFileProperties;
1693     end
1694     else if SingleSelectedDep<>nil then begin
1695       PropsGroupBox.Enabled:=not SingleSelectedRemoved;
1696       PropsGroupBox.Caption:=lisPckEditDependencyProperties;
1697       FPropGui.SetMinMaxValues(SingleSelectedDep);
1698       FPropGui.UpdateApplyDependencyButton;
1699     end
1700     else if SingleSelectedDirectory<>nil then begin
1701       //PropsGroupBox.Enabled:=true;
1702       //PropsGroupBox.Caption:=lisPckEditFileProperties;
1703     end
1704     else begin
1705       PropsGroupBox.Enabled:=false;
1706       PropsGroupBox.Caption:=lisPckEditDependencyProperties;
1707     end;
1708   finally
1709     EnableAlign;
1710   end;
1711 end;
1712 
1713 procedure TProjectInspectorForm.UpdateButtons(Immediately: boolean);
1714 var
1715   i: Integer;
1716   TVNode: TTreeNode;
1717   NodeData: TPENodeData;
1718   Item: TObject;
1719   CanRemoveCount: Integer;
1720   CurUnitInfo: TUnitInfo;
1721   CanOpenCount: Integer;
1722 begin
1723   if not CanUpdate(pefNeedUpdateButtons,Immediately) then exit;
1724   CanRemoveCount:=0;
1725   CanOpenCount:=0;
1726   if Assigned(LazProject) then
1727   begin
1728     for i:=0 to ItemsTreeView.SelectionCount-1 do begin
1729       TVNode:=ItemsTreeView.Selections[i];
1730       if not GetNodeDataItem(TVNode,NodeData,Item) then continue;
1731       if Item is TUnitInfo then begin
1732         CurUnitInfo:=TUnitInfo(Item);
1733         inc(CanOpenCount);
1734         if CurUnitInfo<>LazProject.MainUnitInfo then
1735           inc(CanRemoveCount);
1736       end else if Item is TPkgDependency then begin
1737         if not NodeData.Removed and (TPkgDependency(Item).DependencyType=pdtLazarus) then begin
1738           inc(CanRemoveCount);
1739           inc(CanOpenCount);
1740         end;
1741       end;
1742     end;
1743   end;
1744   AddBitBtn.Enabled:=Assigned(LazProject);
1745   RemoveBitBtn.Enabled:=(CanRemoveCount>0);
1746   OpenButton.Enabled:=(CanOpenCount>0);
1747   OptionsBitBtn.Enabled:=Assigned(LazProject);
1748 end;
1749 
1750 procedure TProjectInspectorForm.UpdatePending;
1751 begin
1752   ItemsTreeView.BeginUpdate;
1753   try
1754     if pefNeedUpdateTitle in FFlags then
1755       UpdateTitle(True);
1756     if pefNeedUpdateFiles in FFlags then
1757       UpdateFiles(True);
1758     if pefNeedUpdateRequiredPkgs in FFlags then
1759       UpdateRequiredPackages(True);
1760     if pefNeedUpdateProperties in FFlags then
1761       UpdateProperties(True);
1762     if pefNeedUpdateButtons in FFlags then
1763       UpdateButtons(True);
1764     //if pefNeedUpdateApplyDependencyButton in fFlags then
1765     //  FPropGui.UpdateApplyDependencyButton(true);
1766     IdleConnected:=false;
1767   finally
1768     ItemsTreeView.EndUpdate;
1769   end;
1770 end;
1771 
CanUpdatenull1772 function TProjectInspectorForm.CanUpdate(Flag: TPEFlag; Immediately: boolean): boolean;
1773 begin
1774   Result:=false;
1775   if csDestroying in ComponentState then exit;
1776   if (FUpdateLock>0) and not Immediately then begin
1777     Include(fFlags,Flag);
1778     IdleConnected:=true;
1779   end else begin
1780     Exclude(fFlags,Flag);
1781     Result:=true;
1782   end;
1783 end;
1784 
GetNodeItemnull1785 function TProjectInspectorForm.GetNodeItem(NodeData: TPENodeData): TObject;
1786 begin
1787   Result:=nil;
1788   if (LazProject=nil) or (NodeData=nil) then exit;
1789   case NodeData.Typ of
1790   penFile:
1791     if NodeData.Removed then
1792       Result:=nil
1793     else
1794       Result:=LazProject.UnitInfoWithFilename(NodeData.Name,[pfsfOnlyProjectFiles]);
1795   penDependency:
1796     if NodeData.Removed then
1797       Result:=LazProject.FindRemovedDependencyByName(NodeData.Name)
1798     else
1799       Result:=LazProject.FindDependencyByName(NodeData.Name);
1800   end;
1801 end;
1802 
GetNodeDataItemnull1803 function TProjectInspectorForm.GetNodeDataItem(TVNode: TTreeNode; out
1804   NodeData: TPENodeData; out Item: TObject): boolean;
1805 begin
1806   Result:=false;
1807   Item:=nil;
1808   NodeData:=GetNodeData(TVNode);
1809   Item:=GetNodeItem(NodeData);
1810   Result:=Item<>nil;
1811 end;
1812 
1813 { TSetBuildModeToolButton.TBuildModeMenu }
1814 
1815 procedure TSetBuildModeToolButton.TBuildModeMenu.DoPopup(Sender: TObject);
1816 var
1817   CurIndex: Integer;
1818   i: Integer;
1819 
1820   procedure AddMode(BuildModeIndex: Integer; CurMode: TProjectBuildMode);
1821   var
1822     AMenuItem: TBuildModeMenuItem;
1823   begin
1824     if Items.Count > CurIndex then
1825       AMenuItem := Items[CurIndex] as TBuildModeMenuItem
1826     else
1827     begin
1828       AMenuItem := TBuildModeMenuItem.Create(Self);
1829       AMenuItem.Name := Name + 'Mode' + IntToStr(CurIndex);
1830       Items.Add(AMenuItem);
1831     end;
1832     AMenuItem.BuildModeIndex := BuildModeIndex;
1833     AMenuItem.Caption := CurMode.GetCaption;
1834     AMenuItem.Checked := (Project1<>nil) and (Project1.ActiveBuildMode=CurMode);
1835     AMenuItem.ShowAlwaysCheckable:=true;
1836     inc(CurIndex);
1837   end;
1838 
1839 begin
1840   // fill the PopupMenu
1841   CurIndex := 0;
1842   if Project1<>nil then
1843     for i:=0 to Project1.BuildModes.Count-1 do
1844       AddMode(i, Project1.BuildModes[i]);
1845   // remove unused menuitems
1846   while Items.Count > CurIndex do
1847     Items[Items.Count - 1].Free;
1848 
1849   inherited DoPopup(Sender);
1850 end;
1851 
1852 { TSetBuildModeToolButton.TBuildModeMenuItem }
1853 
1854 procedure TSetBuildModeToolButton.TBuildModeMenuItem.Click;
1855 var
1856   NewMode: TProjectBuildMode;
1857 begin
1858   inherited Click;
1859 
1860   NewMode := Project1.BuildModes[BuildModeIndex];
1861   if NewMode = Project1.ActiveBuildMode then exit;
1862   if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin
1863     IDEMessageDialog(dlgMsgWinColorUrgentError,
1864       lisYouCanNotChangeTheBuildModeWhileCompiling,
1865       mtError,[mbOk]);
1866     exit;
1867   end;
1868 
1869   Project1.ActiveBuildMode := NewMode;
1870   Project1.DefineTemplates.AllChanged(false);
1871   IncreaseCompilerParseStamp;
1872   MainBuildBoss.SetBuildTargetProject1(false);
1873   MainIDE.UpdateCaption;
1874   if Assigned(ProjInspector) then
1875     ProjInspector.UpdateTitle;
1876 end;
1877 
1878 { TSetBuildModeToolButton }
1879 
1880 procedure TSetBuildModeToolButton.DoOnAdded;
1881 begin
1882   inherited DoOnAdded;
1883 
1884   DropdownMenu := TBuildModeMenu.Create(Self);
1885   Style := tbsDropDown;
1886 end;
1887 
1888 end.
1889 
1890