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   Abstract:
22     Functions to deal with different kinds of source files like
23      units, projects, packages and their related IDE features.
24     The code is copied and refactored from the huge main.pp. The goal was to not
25     call methods defined in TMainIDE but there are still some calls doing it.
26 }
27 unit SourceFileManager;
28 
29 {$mode objfpc}{$H+}
30 {$MODESWITCH ADVANCEDRECORDS}
31 
32 interface
33 
34 uses
35   Classes, SysUtils, typinfo, math, fpjson, Laz_AVL_Tree,
36   // LCL
37   Controls, Forms, Dialogs, LCLIntf, LCLType, LclStrConsts,
38   LResources, LCLMemManager,
39   // LazUtils
40   LConvEncoding, LazFileCache, FileUtil, LazFileUtils, LazLoggerBase, LazUtilities,
41   LazUTF8, LazTracer, AvgLvlTree,
42   // Codetools
43   BasicCodeTools, CodeToolsStructs, CodeToolManager, FileProcs, DefineTemplates,
44   CodeCache, CodeTree, FindDeclarationTool, KeywordFuncLists,
45   // IdeIntf
46   IDEDialogs, PropEdits, IDEMsgIntf, LazIDEIntf, MenuIntf, NewItemIntf,
47   IDEWindowIntf, ProjectIntf, PackageIntf, PackageDependencyIntf, FormEditingIntf,
48   IDEExternToolIntf, ObjectInspector, UnitResources, ComponentReg,
49   SrcEditorIntf, EditorSyntaxHighlighterDef,
50   // IDE
51   IDEProcs, DialogProcs, IDEProtocol, LazarusIDEStrConsts, NewDialog, NewProjectDlg,
52   MainBase, MainBar, MainIntf, Project, ProjectDefs, ProjectInspector, CompilerOptions,
53   SourceSynEditor, SourceEditor, EditorOptions, EnvironmentOpts, CustomFormEditor,
54   ControlSelection, FormEditor, EmptyMethodsDlg, BaseDebugManager, TransferMacros,
55   BuildManager, EditorMacroListViewer, FindRenameIdentifier, BuildModesManager,
56   ViewUnit_Dlg, InputHistory, CheckLFMDlg, etMessagesWnd,
57   ConvCodeTool, BasePkgManager, PackageDefs, PackageSystem, Designer, DesignerProcs;
58 
59 type
60 
61   TBookmarkCommandsStamp = record
62   private
63     FBookmarksStamp: Int64;
64   public
Changednull65     function Changed(ABookmarksStamp: Int64): Boolean;
66   end;
67 
68   TFileCommandsStamp = record
69   private
70     FSrcEdit: TSourceEditor;
71   public
Changednull72     function Changed(ASrcEdit: TSourceEditor): Boolean;
73   end;
74 
75   TProjectCommandsStamp = record
76   private
77     FUnitInfo: TUnitInfo;
78     FProjectChangeStamp: Int64;
79     FProjectSessionChangeStamp: Int64;
80     FCompilerParseStamp: integer;
81     FBuildMacroChangeStamp: integer;
82   public
Changednull83     function Changed(AUnitInfo: TUnitInfo): Boolean;
84   end;
85 
86   TPackageCommandsStamp = record
87   private
88     FUnitInfo: TUnitInfo;
89     FPackagesChangeStamp: Int64;
90   public
Changednull91     function Changed(AUnitInfo: TUnitInfo): Boolean;
92   end;
93 
94   TSourceEditorTabCommandsStamp = record
95   private
96     FSrcEdit: TSourceEditor;
97     FSrcEditLocked: Boolean;
98     FSourceNotebook: TSourceNotebook;
99     FPageIndex, FPageCount: Integer;
100   public
Changednull101     function Changed(ASrcEdit: TSourceEditor): Boolean;
102   end;
103 
104   TSourceEditorCommandsStamp = record
105   private
106     FSrcEdit: TSourceEditor;
107     FDisplayState: TDisplayState;
108     FEditorComponentStamp: int64;
109     FEditorCaretStamp: int64;
110 
111     FDesigner: TDesigner;
112     FDesignerSelectionStamp: int64;
113     FDesignerStamp: int64;
114   public
Changednull115     function Changed(ASrcEdit: TSourceEditor; ADesigner: TDesigner;
116       ADisplayState: TDisplayState): Boolean;
117   end;
118 
119   { TFileOpener }
120 
121   TFileOpener = class
122   private
123     FFileName: string;
124     FUseWindowID: Boolean;
125     FPageIndex: integer;
126     FWindowIndex: integer;
127     // Used by OpenEditorFile
128     FUnitIndex: integer;
129     FEditorInfo: TUnitEditorInfo;
130     FNewEditorInfo: TUnitEditorInfo;
131     FFlags: TOpenFlags;
132     FUnknownFile: boolean;
133     FNewUnitInfo: TUnitInfo;
134     // Used by OpenFileAtCursor
135     FActiveSrcEdit: TSourceEditor;
136     FActiveUnitInfo: TUnitInfo;
137     FIsIncludeDirective: boolean;
OpenFileInSourceEditornull138     function OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo): TModalResult;
139     // Used by GetAvailableUnitEditorInfo
AvailSrcWindowIndexnull140     function AvailSrcWindowIndex(AnUnitInfo: TUnitInfo): Integer;
141     // Used by OpenEditorFile
OpenResourcenull142     function OpenResource: TModalResult;
ChangeEditorPagenull143     function ChangeEditorPage: TModalResult;
144     procedure CheckInternalFile;
CheckRevertnull145     function CheckRevert: TModalResult;
OpenKnownnull146     function OpenKnown: TModalResult;
OpenUnknownnull147     function OpenUnknown: TModalResult;
OpenUnknownFilenull148     function OpenUnknownFile: TModalResult;
OpenNotExistingFilenull149     function OpenNotExistingFile: TModalResult;
PrepareFilenull150     function PrepareFile: TModalResult;
PrepareRevertnull151     function PrepareRevert(DiskFilename: String): TModalResult;
ResolvePossibleSymlinknull152     function ResolvePossibleSymlink: TModalResult;
153     // Used by OpenFileAtCursor
CheckIfIncludeDirectiveInFrontnull154     function CheckIfIncludeDirectiveInFront(const Line: string; X: integer): boolean;
FindFilenull155     function FindFile(SearchPath: String): Boolean;
GetFilenameAtRowColnull156     function GetFilenameAtRowCol(XY: TPoint): string;
157   public
158     // These methods have a global wrapper
GetAvailableUnitEditorInfonull159     function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
160       ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
OpenEditorFilenull161     function OpenEditorFile(APageIndex, AWindowIndex: integer;
162       AEditorInfo: TUnitEditorInfo; AFlags: TOpenFlags): TModalResult;
OpenFileAtCursornull163     function OpenFileAtCursor: TModalResult;
OpenMainUnitnull164     function OpenMainUnit: TModalResult;
RevertMainUnitnull165     function RevertMainUnit: TModalResult;
166   end;
167 
168 
CreateSrcEditPageNamenull169 function CreateSrcEditPageName(const AnUnitName, AFilename: string;
170   IgnoreEditor: TSourceEditor): string;
171 procedure UpdateDefaultPasFileExt;
172 
173 // Wrappers for TFileOpener methods.
174 // WindowIndex is WindowID
GetAvailableUnitEditorInfonull175 function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
176   ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
OpenEditorFilenull177 function OpenEditorFile(AFileName: string; PageIndex, WindowIndex: integer;
178   AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
OpenFileAtCursornull179 function OpenFileAtCursor(ActiveSrcEdit: TSourceEditor;
180   ActiveUnitInfo: TUnitInfo): TModalResult;
OpenMainUnitnull181 function OpenMainUnit(PageIndex, WindowIndex: integer;
182   Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
RevertMainUnitnull183 function RevertMainUnit: TModalResult;
184 // recent
185 procedure AddRecentProjectFile(const AFilename: string);
186 procedure RemoveRecentProjectFile(const AFilename: string);
187 procedure UpdateSourceNames;
CheckEditorNeedsSavenull188 function CheckEditorNeedsSave(AEditor: TSourceEditorInterface;
189     IgnoreSharedEdits: Boolean): Boolean;
190 procedure ArrangeSourceEditorAndMessageView(PutOnTop: boolean);
191 // files/units/projects
SomethingOfProjectIsModifiednull192 function SomethingOfProjectIsModified(Verbose: boolean = false): boolean;
NewFilenull193 function NewFile(NewFileDescriptor: TProjectFileDescriptor;
194   var NewFilename: string; NewSource: string;
195   NewFlags: TNewFlags; NewOwner: TObject): TModalResult;
NewOthernull196 function NewOther: TModalResult;
NewUnitOrFormnull197 function NewUnitOrForm(Template: TNewIDEItemTemplate;
198   DefaultDesc: TProjectFileDescriptor): TModalResult;
199 procedure CreateFileDialogFilterForSourceEditorFiles(Filter: string;
200     out AllEditorMask, AllMask: string);
SaveEditorFilenull201 function SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult;
SaveEditorFilenull202 function SaveEditorFile(const Filename: string; Flags: TSaveFlags): TModalResult;
CloseEditorFilenull203 function CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags):TModalResult;
CloseEditorFilenull204 function CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult;
205 // interactive unit selection
SelectProjectItemsnull206 function SelectProjectItems(ItemList: TViewUnitEntries; ItemType: TIDEProjectItem;
207   MultiSelect: boolean; var MultiSelectCheckedState: Boolean): TModalResult;
SelectUnitComponentsnull208 function SelectUnitComponents(DlgCaption: string; ItemType: TIDEProjectItem;
209   Files: TStringList; MultiSelect: boolean;
210   var MultiSelectCheckedState: Boolean): TModalResult;
211 // unit search
FindUnitFileImplnull212 function FindUnitFileImpl(const AFilename: string; TheOwner: TObject = nil;
213                           Flags: TFindUnitFileFlags = []): string;
FindSourceFileImplnull214 function FindSourceFileImpl(const AFilename, BaseDirectory: string;
215                             Flags: TFindSourceFlags): string;
FindUnitsOfOwnerImplnull216 function FindUnitsOfOwnerImpl(TheOwner: TObject; Flags: TFindUnitsOfOwnerFlags): TStrings;
217 // project
AddUnitToProjectnull218 function AddUnitToProject(const AEditor: TSourceEditorInterface): TModalResult;
AddActiveUnitToProjectnull219 function AddActiveUnitToProject: TModalResult;
RemoveFromProjectDialognull220 function RemoveFromProjectDialog: TModalResult;
InitNewProjectnull221 function InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult;
InitOpenedProjectFilenull222 function InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult;
223 procedure NewProjectFromFile;
CreateProjectForProgramnull224 function CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
InitProjectForProgramnull225 function InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
SaveProjectnull226 function SaveProject(Flags: TSaveFlags): TModalResult;
SaveProjectIfChangednull227 function SaveProjectIfChanged: TModalResult;
CloseProjectnull228 function CloseProject: TModalResult;
229 procedure OpenProject(aMenuItem: TIDEMenuItem);
CompleteLoadingProjectInfonull230 function CompleteLoadingProjectInfo: TModalResult;
231 procedure CloseAll;
232 procedure InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook);
233 // designer
DesignerUnitIsVirtualnull234 function DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean;
CheckLFMInEditornull235 function CheckLFMInEditor(LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult;
LoadLFMnull236 function LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags;
237                    CloseFlags: TCloseFlags): TModalResult;
LoadLFMnull238 function LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer;
239                    OpenFlags: TOpenFlags;
240                    CloseFlags: TCloseFlags): TModalResult;
OpenComponentnull241 function OpenComponent(const UnitFilename: string; OpenFlags: TOpenFlags;
242     CloseFlags: TCloseFlags; out Component: TComponent): TModalResult;
UpdateUnitInfoResourceBaseClassnull243 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
CloseUnitComponentnull244 function CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
CloseDependingUnitComponentsnull245 function CloseDependingUnitComponents(AnUnitInfo: TUnitInfo;
246                                       Flags: TCloseFlags): TModalResult;
UnitComponentIsUsednull247 function UnitComponentIsUsed(AnUnitInfo: TUnitInfo;
248                              CheckHasDesigner: boolean): boolean;
RemoveFilesFromProjectnull249 function RemoveFilesFromProject(UnitInfos: TFPList): TModalResult;
AskSaveProjectnull250 function AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult;
SaveEditorChangesToCodeCachenull251 function SaveEditorChangesToCodeCache(AEditor: TSourceEditorInterface): boolean;
252 
253 
254 // These are local functions. Forward reference is needed for most of them.
AskToSaveEditorsnull255 //  function AskToSaveEditors(EditorList: TList): TModalResult;
256 //  function CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult;
257 //  function FileExistsInIDE(const Filename: string;
258 //    SearchFlags: TProjectFileSearchFlags): boolean;
259 //new unit
260   function CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor;
261       NewOwner: TObject; NewFilename: string; var NewCodeBuffer: TCodeBuffer;
262       var NewUnitName: string): TModalResult;
CreateNewFormnull263   function CreateNewForm(NewUnitInfo: TUnitInfo;
264       AncestorType: TPersistentClass; ResourceCode: TCodeBuffer;
265       UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult;
NewUniqueComponentNamenull266   function NewUniqueComponentName(Prefix: string): string;
267 //save unit
ShowSaveFileAsDialognull268   function ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo;
269       var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult;
SaveUnitComponentnull270   function SaveUnitComponent(AnUnitInfo: TUnitInfo;
271       LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult;
RemoveLooseEventsnull272   function RemoveLooseEvents(AnUnitInfo: TUnitInfo): TModalResult;
RenameUnitnull273   function RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string;
274       var LFMCode, LRSCode: TCodeBuffer): TModalResult;
RenameUnitLowerCasenull275   function RenameUnitLowerCase(AnUnitInfo: TUnitInfo; AskUser: boolean): TModalresult;
ReplaceUnitUsenull276   function ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string;
277                           IgnoreErrors, Quiet, Confirm: boolean): TModalResult;
278 //designer
LoadResourceFilenull279   function LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer;
280       AutoCreateResourceCode, ShowAbort: boolean): TModalResult;
FindBaseComponentClassnull281 //  function FindBaseComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName,
282 //      DescendantClassName: string; out AComponentClass: TComponentClass): boolean;
283   function LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo;
284       const aComponentClassName: string; OpenFlags: TOpenFlags;
285       out AncestorClass: TComponentClass; out AncestorUnitInfo: TUnitInfo): TModalResult;
FindComponentClassnull286 //  function FindComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName: string;
287 //      Quiet: boolean; out ComponentUnitInfo: TUnitInfo; out AComponentClass: TComponentClass;
288 //      out LFMFilename: string; out AncestorClass: TComponentClass): TModalResult;
289   function LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo;
290       const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean;
291       out AComponentClass: TComponentClass; out ComponentUnitInfo: TUnitInfo;
292       out AncestorClass: TComponentClass; const IgnoreBtnText: string = ''): TModalResult;
LoadIDECodeBuffernull293   function LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer;
294       const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult;
295 //save project
ShowSaveProjectAsDialognull296   function ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult;
SaveProjectInfonull297   function SaveProjectInfo(var Flags: TSaveFlags): TModalResult;
298   procedure GetMainUnit(out MainUnitInfo: TUnitInfo;
299       out MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean);
300   procedure SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo);
301   procedure SaveSourceEditorProjectSpecificSettings;
302   procedure UpdateProjectResourceInfo;
303 
304 
305 implementation
306 
CreateSrcEditPageNamenull307 function CreateSrcEditPageName(const AnUnitName, AFilename: string;
308   IgnoreEditor: TSourceEditor): string;
309 begin
310   Result:=AnUnitName;
311   if Result='' then
312     Result:=AFilename;
313   if FilenameIsPascalUnit(Result) then
314     Result:=ExtractFileNameOnly(Result)
315   else
316     Result:=ExtractFileName(Result);
317   Result:=SourceEditorManager.FindUniquePageName(Result,IgnoreEditor);
318 end;
319 
320 procedure UpdateDefaultPasFileExt;
321 var
322   DefPasExt: string;
323 begin
324   // change default pascal file extensions
325   DefPasExt:=PascalExtension[EnvironmentOptions.PascalFileExtension];
326   if LazProjectFileDescriptors<>nil then
327     LazProjectFileDescriptors.DefaultPascalFileExt:=DefPasExt;
328 end;
329 
330 // Wrappers for TFileOpener methods.
331 
GetAvailableUnitEditorInfonull332 function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
333   ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
334 var
335   Opener: TFileOpener;
336 begin
337   Opener := TFileOpener.Create;
338   try
339     Result := Opener.GetAvailableUnitEditorInfo(AnUnitInfo,ACaretPoint,WantedTopLine);
340   finally
341     Opener.Free;
342   end;
343 end;
344 
OpenEditorFilenull345 function OpenEditorFile(AFileName: string; PageIndex, WindowIndex: integer;
346   AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
347 var
348   Opener: TFileOpener;
349 begin
350   Opener := TFileOpener.Create;
351   try
352     Opener.FFileName := AFileName;
353     Opener.FUseWindowID := UseWindowID;
354     Result := Opener.OpenEditorFile(PageIndex,WindowIndex,AEditorInfo,Flags);
355   finally
356     Opener.Free;
357   end;
358 end;
359 
OpenFileAtCursornull360 function OpenFileAtCursor(ActiveSrcEdit: TSourceEditor; ActiveUnitInfo: TUnitInfo): TModalResult;
361 var
362   Opener: TFileOpener;
363 begin
364   Opener := TFileOpener.Create;
365   try
366     Opener.FActiveSrcEdit := ActiveSrcEdit;
367     Opener.FActiveUnitInfo := ActiveUnitInfo;
368     Result := Opener.OpenFileAtCursor;
369   finally
370     Opener.Free;
371   end;
372 end;
373 
OpenMainUnitnull374 function OpenMainUnit(PageIndex, WindowIndex: integer;
375   Flags: TOpenFlags; UseWindowID: Boolean): TModalResult;
376 var
377   Opener: TFileOpener;
378 begin
379   Opener := TFileOpener.Create;
380   try
381     Opener.FPageIndex := PageIndex;
382     Opener.FWindowIndex := WindowIndex;
383     Opener.FFlags := Flags;
384     Opener.FUseWindowID := UseWindowID;
385     Result := Opener.OpenMainUnit;
386   finally
387     Opener.Free;
388   end;
389 end;
390 
RevertMainUnitnull391 function RevertMainUnit: TModalResult;
392 var
393   Opener: TFileOpener;
394 begin
395   Opener := TFileOpener.Create;
396   try
397     Result := Opener.RevertMainUnit;
398   finally
399     Opener.Free;
400   end;
401 end;
402 
403 { TBookmarkCommandsStamp }
404 
TBookmarkCommandsStamp.Changednull405 function TBookmarkCommandsStamp.Changed(ABookmarksStamp: Int64): Boolean;
406 begin
407   Result := (FBookmarksStamp <> ABookmarksStamp);
408   if Result then
409     FBookmarksStamp := ABookmarksStamp;
410 end;
411 
412 { TFileCommandsStamp }
413 
TFileCommandsStamp.Changednull414 function TFileCommandsStamp.Changed(ASrcEdit: TSourceEditor): Boolean;
415 begin
416   Result := not(
417         (FSrcEdit = ASrcEdit)
418     );
419 
420   if not Result then Exit;
421 
422   FSrcEdit := ASrcEdit;
423 end;
424 
425 { TProjectCommandsStamp }
426 
Changednull427 function TProjectCommandsStamp.Changed(AUnitInfo: TUnitInfo): Boolean;
428 var
429   CurProjectChangeStamp, CurProjectSessionChangeStamp: Integer;
430 begin
431   if Project1=nil then
432   begin
433     CurProjectChangeStamp := LUInvalidChangeStamp;
434     CurProjectSessionChangeStamp := LUInvalidChangeStamp;
435   end else
436   begin
437     CurProjectChangeStamp := Project1.ChangeStamp;
438     CurProjectSessionChangeStamp := Project1.SessionChangeStamp;
439   end;
440   Result := not(
441         (FUnitInfo = AUnitInfo)
442     and (FProjectChangeStamp = CurProjectChangeStamp)
443     and (FProjectSessionChangeStamp = CurProjectSessionChangeStamp)
444     and (FCompilerParseStamp = CompilerParseStamp)
445     and (FBuildMacroChangeStamp = BuildMacroChangeStamp)
446     );
447 
448   if not Result then Exit;
449 
450   FUnitInfo := AUnitInfo;
451   FProjectChangeStamp := CurProjectChangeStamp;
452   FProjectSessionChangeStamp := CurProjectSessionChangeStamp;
453   FCompilerParseStamp := CompilerParseStamp;
454   FBuildMacroChangeStamp := BuildMacroChangeStamp;
455 end;
456 
457 { TPackageCommandsStamp }
458 
TPackageCommandsStamp.Changednull459 function TPackageCommandsStamp.Changed(AUnitInfo: TUnitInfo): Boolean;
460 begin
461   Result := not(
462         (FUnitInfo = AUnitInfo)
463     and (FPackagesChangeStamp = PackageGraph.ChangeStamp)
464     );
465 
466   if not Result then Exit;
467 
468   FUnitInfo := AUnitInfo;
469   FPackagesChangeStamp := PackageGraph.ChangeStamp;
470 end;
471 
472 { TSourceEditorTabCommandsStamp }
473 
Changednull474 function TSourceEditorTabCommandsStamp.Changed(ASrcEdit: TSourceEditor): Boolean;
475 begin
476   Result := not(
477         (FSrcEdit = ASrcEdit)
478     and ((ASrcEdit = nil) or (
479             (FSrcEditLocked = ASrcEdit.IsLocked)
480         and (FSourceNotebook = ASrcEdit.SourceNotebook)
481         and (FPageIndex = ASrcEdit.SourceNotebook.PageIndex)
482         and (FPageCount = ASrcEdit.SourceNotebook.PageCount)))
483     );
484 
485   if not Result then Exit;
486 
487   FSrcEdit := ASrcEdit;
488   if ASrcEdit<>nil then
489   begin
490     FSrcEditLocked := ASrcEdit.IsLocked;
491     FSourceNotebook := ASrcEdit.SourceNotebook;
492     FPageIndex := ASrcEdit.SourceNotebook.PageIndex;
493     FPageCount := ASrcEdit.SourceNotebook.PageCount;
494   end;
495 end;
496 
497 { TSourceEditorCommandsStamp }
498 
TSourceEditorCommandsStamp.Changednull499 function TSourceEditorCommandsStamp.Changed(ASrcEdit: TSourceEditor;
500   ADesigner: TDesigner; ADisplayState: TDisplayState): Boolean;
501 begin
502   Result := not(
503         (FSrcEdit = ASrcEdit)
504     and (FDesigner = ADesigner)
505     and (FDisplayState = ADisplayState)
506     and ((ASrcEdit = nil) or (
507             (FEditorComponentStamp = ASrcEdit.EditorComponent.ChangeStamp)
508         and (FEditorCaretStamp = ASrcEdit.EditorComponent.CaretStamp)))
509     and ((ADesigner = nil) or (
510             (FDesignerSelectionStamp = ADesigner.Selection.ChangeStamp)
511         and (FDesignerStamp = ADesigner.ChangeStamp)))
512     );
513 
514   if not Result then Exit;
515 
516   FSrcEdit := ASrcEdit;
517   FDesigner := ADesigner;
518   FDisplayState := ADisplayState;
519   if ASrcEdit<>nil then
520   begin
521     FEditorComponentStamp := ASrcEdit.EditorComponent.ChangeStamp;
522     FEditorCaretStamp := ASrcEdit.EditorComponent.CaretStamp;
523   end;
524   if ADesigner<>nil then
525   begin
526     FDesignerSelectionStamp := ADesigner.Selection.ChangeStamp;
527     FDesignerStamp := ADesigner.ChangeStamp;
528   end;
529 end;
530 
531 //==============================================================================
532 
533 { TFileOpener }
534 
TFileOpener.OpenFileInSourceEditornull535 function TFileOpener.OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo): TModalResult;
536 var
537   NewSrcEdit: TSourceEditor;
538   AFilename: string;
539   NewCaretXY: TPoint;
540   NewTopLine: LongInt;
541   NewLeftChar: LongInt;
542   NewErrorLine: LongInt;
543   NewExecutionLine: LongInt;
544   FoldState: String;
545   SrcNotebook: TSourceNotebook;
546   AnUnitInfo: TUnitInfo;
547   AShareEditor: TSourceEditor;
548 begin
549   //debugln(['TFileOpener.OpenFileInSourceEditor ',AnEditorInfo.UnitInfo.Filename,' Window=',WindowIndex,'/',SourceEditorManager.SourceWindowCount,' Page=',PageIndex]);
550   AnUnitInfo := AnEditorInfo.UnitInfo;
551   AFilename:=AnUnitInfo.Filename;
552   if (FWindowIndex < 0) then
553     SrcNotebook := SourceEditorManager.ActiveOrNewSourceWindow
554   else
555   if FUseWindowID then begin
556     SrcNotebook := SourceEditorManager.SourceWindowWithID(FWindowIndex);
557     FWindowIndex := SourceEditorManager.IndexOfSourceWindow(SrcNotebook);
558   end
559   else
560   if (FWindowIndex >= SourceEditorManager.SourceWindowCount) then begin
561     SrcNotebook := SourceEditorManager.NewSourceWindow;
562   end
563   else
564     SrcNotebook := SourceEditorManager.SourceWindows[FWindowIndex];
565 
566   // get syntax highlighter type
567   if (uifInternalFile in AnUnitInfo.Flags) then
568     AnUnitInfo.UpdateDefaultHighlighter(lshFreePascal)
569   else
570     AnUnitInfo.UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(AFilename));
571 
572   SrcNotebook.IncUpdateLock;
573   try
574     //DebugLn(['TFileOpener.OpenFileInSourceEditor Revert=',ofRevert in Flags,' ',AnUnitInfo.Filename,' PageIndex=',PageIndex]);
575     if (not (ofRevert in FFlags)) or (FPageIndex<0) then begin
576       // create a new source editor
577 
578       // update marks and cursor positions in Project1, so that merging the old
579       // settings during restoration will work
580       SaveSourceEditorProjectSpecificSettings;
581       AShareEditor := nil;
582       if AnUnitInfo.OpenEditorInfoCount > 0 then
583         AShareEditor := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent);
584       NewSrcEdit:=SrcNotebook.NewFile(
585         CreateSrcEditPageName(AnUnitInfo.Unit_Name, AFilename, AShareEditor),
586         AnUnitInfo.Source, False, AShareEditor);
587       NewSrcEdit.EditorComponent.BeginUpdate;
588       MainIDEBar.itmFileClose.Enabled:=True;
589       MainIDEBar.itmFileCloseAll.Enabled:=True;
590       NewCaretXY := AnEditorInfo.CursorPos;
591       NewTopLine := AnEditorInfo.TopLine;
592       FoldState := AnEditorInfo.FoldState;
593       NewLeftChar:=1;
594       NewErrorLine:=-1;
595       NewExecutionLine:=-1;
596     end else begin
597       // revert code in existing source editor
598       NewSrcEdit:=SourceEditorManager.SourceEditorsByPage[FWindowIndex, FPageIndex];
599       NewCaretXY:=NewSrcEdit.EditorComponent.CaretXY;
600       NewTopLine:=NewSrcEdit.EditorComponent.TopLine;
601       FoldState := NewSrcEdit.EditorComponent.FoldState;
602       NewLeftChar:=NewSrcEdit.EditorComponent.LeftChar;
603       NewErrorLine:=NewSrcEdit.ErrorLine;
604       NewExecutionLine:=NewSrcEdit.ExecutionLine;
605       NewSrcEdit.EditorComponent.BeginUpdate;
606       if NewSrcEdit.CodeBuffer=AnUnitInfo.Source then begin
607         AnUnitInfo.Source.AssignTo(NewSrcEdit.EditorComponent.Lines,true);
608       end else
609         NewSrcEdit.CodeBuffer:=AnUnitInfo.Source;
610       AnUnitInfo.ClearModifieds;
611       //DebugLn(['TFileOpener.OpenFileInSourceEditor NewCaretXY=',dbgs(NewCaretXY),' NewTopLine=',NewTopLine]);
612     end;
613 
614     NewSrcEdit.IsLocked := AnEditorInfo.IsLocked;
615     AnEditorInfo.EditorComponent := NewSrcEdit;
616     //debugln(['TFileOpener.OpenFileInSourceEditor ',AnUnitInfo.Filename,' ',AnUnitInfo.EditorIndex]);
617 
618     // restore source editor settings
619     DebugBoss.DoRestoreDebuggerMarks(AnUnitInfo);
620     NewSrcEdit.SyntaxHighlighterType := AnEditorInfo.SyntaxHighlighter;
621     NewSrcEdit.EditorComponent.AfterLoadFromFile;
622     try
623       NewSrcEdit.EditorComponent.FoldState := FoldState;
624     except
625       IDEMessageDialog(lisError, lisFailedToLoadFoldStat, mtError, [mbOK]);
626     end;
627 
628     NewSrcEdit.EditorComponent.CaretXY:=NewCaretXY;
629     NewSrcEdit.EditorComponent.TopLine:=NewTopLine;
630     NewSrcEdit.EditorComponent.LeftChar:=NewLeftChar;
631     NewSrcEdit.ErrorLine:=NewErrorLine;
632     NewSrcEdit.ExecutionLine:=NewExecutionLine;
633     NewSrcEdit.ReadOnly:=AnUnitInfo.ReadOnly;
634     NewSrcEdit.Modified:=false;
635 
636     // mark unit as loaded
637     NewSrcEdit.EditorComponent.EndUpdate;
638     AnUnitInfo.Loaded:=true;
639   finally
640     SrcNotebook.DecUpdateLock;
641   end;
642 
643   // update statusbar and focus editor
644   if (not (ofProjectLoading in FFlags)) then begin
645     SourceEditorManager.ActiveEditor := NewSrcEdit;
646     SourceEditorManager.ShowActiveWindowOnTop(True);
647   end;
648   SrcNoteBook.UpdateStatusBar;
649   SrcNotebook.BringToFront;
650 
651   Result:=mrOk;
652 end;
653 
TFileOpener.AvailSrcWindowIndexnull654 function TFileOpener.AvailSrcWindowIndex(AnUnitInfo: TUnitInfo): Integer;
655 var
656   i: Integer;
657 begin
658   Result := -1;
659   i := 0;
660   if AnUnitInfo.OpenEditorInfoCount > 0 then
661     while (i < SourceEditorManager.SourceWindowCount) and
662           (SourceEditorManager.SourceWindowByLastFocused[i].IndexOfEditorInShareWith
663              (TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent)) >= 0)
664     do
665       inc(i);
666   if i < SourceEditorManager.SourceWindowCount then
667     Result := SourceEditorManager.IndexOfSourceWindow(SourceEditorManager.SourceWindowByLastFocused[i]);
668 end;
669 
GetAvailableUnitEditorInfonull670 function TFileOpener.GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
671   ACaretPoint: TPoint; WantedTopLine: integer): TUnitEditorInfo;
672 
EditorMatchesnull673   function EditorMatches(AEditInfo: TUnitEditorInfo;
674      AAccess: TEditorOptionsEditAccessOrderEntry; ALockRun: Integer = 0): Boolean;
675   var
676     AEdit: TSourceEditor;
677   begin
678     AEdit := TSourceEditor(AEditInfo.EditorComponent);
679     Result := False;
680     case AAccess.SearchLocked of
681       eoeaIgnoreLock: ;
682       eoeaLockedOnly:   if not AEdit.IsLocked then exit;
683       eoeaUnlockedOnly: if AEdit.IsLocked then exit;
684       eoeaLockedFirst:  if (not AEdit.IsLocked) and (ALockRun = 0) then exit;
685       eoeaLockedLast:   if (AEdit.IsLocked) and (ALockRun = 0) then exit;
686     end;
687     case AAccess.SearchInView of
688       eoeaIgnoreInView: ;
689       eoeaInViewOnly:   if not AEdit.IsCaretOnScreen(ACaretPoint, False) then exit;
690       eoeaInViewSoftCenterOnly: if not AEdit.IsCaretOnScreen(ACaretPoint, True) then exit;
691     end;
692     Result := True;
693   end;
694 
695 var
696   i, j, w, LockRun: Integer;
697   Access: TEditorOptionsEditAccessOrderEntry;
698 begin
699   Result := nil;
700   // Check for already open Editor. If there is none, then it must be opened in OpenEditorFile
701   if AnUnitInfo.OpenEditorInfoCount = 0 then exit;
702   for i := 0 to EditorOpts.MultiWinEditAccessOrder.Count - 1 do begin
703     Access := EditorOpts.MultiWinEditAccessOrder[i];
704     if not Access.Enabled then continue;
705     LockRun := 1;
706     if Access.SearchLocked in [eoeaLockedFirst, eoeaLockedLast] then LockRun := 0;
707     repeat
708       case Access.RealSearchOrder of
709         eoeaOrderByEditFocus, eoeaOrderByListPref:
710           begin
711             for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
712               if EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
713                 Result := AnUnitInfo.OpenEditorInfo[j];
714                 break;
715               end;
716           end;
717         eoeaOrderByWindowFocus:
718           begin
719             for w := 0 to SourceEditorManager.SourceWindowCount - 1 do begin
720               for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
721                 if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
722                     = SourceEditorManager.SourceWindowByLastFocused[w])
723                 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
724                   Result := AnUnitInfo.OpenEditorInfo[j];
725                   break;
726                 end;
727               if Result <> nil then break;
728             end;
729           end;
730         eoeaOrderByOldestEditFocus:
731           begin
732             for j := AnUnitInfo.OpenEditorInfoCount - 1 downto 0 do
733               if EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
734                 Result := AnUnitInfo.OpenEditorInfo[j];
735                 break;
736               end;
737           end;
738         eoeaOrderByOldestWindowFocus:
739           begin
740             for w := SourceEditorManager.SourceWindowCount - 1 downto 0 do begin
741               for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
742                 if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
743                     = SourceEditorManager.SourceWindowByLastFocused[w])
744                 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
745                   Result := AnUnitInfo.OpenEditorInfo[j];
746                   break;
747                 end;
748               if Result <> nil then break;
749             end;
750           end;
751         eoeaOnlyCurrentEdit:
752           begin
753             LockRun := 1;
754             for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
755               if (AnUnitInfo.OpenEditorInfo[j].EditorComponent = SourceEditorManager.ActiveEditor)
756               and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
757                 Result := AnUnitInfo.OpenEditorInfo[j];
758                 break;
759               end;
760           end;
761         eoeaOnlyCurrentWindow:
762           begin
763             LockRun := 1;
764             for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
765               if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
766                   = SourceEditorManager.ActiveSourceWindow)
767               and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
768                 Result := AnUnitInfo.OpenEditorInfo[j];
769                 break;
770               end;
771           end;
772       end;
773       inc(LockRun);
774     until (LockRun > 1) or (Result <> nil);
775     FUseWindowID:=False;
776     FFlags:=[];
777     FPageIndex:=-1;
778     if (Result = nil) then
779       case Access.SearchOpenNew of
780         eoeaNoNewTab: ;
781         eoeaNewTabInExistingWindowOnly:
782           begin
783             FWindowIndex := AvailSrcWindowIndex(AnUnitInfo);
784             if FWindowIndex >= 0 then
785               if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
786                 Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
787           end;
788         eoeaNewTabInNewWindowOnly:
789           begin
790             FWindowIndex := SourceEditorManager.SourceWindowCount;
791             if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
792               Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
793           end;
794         eoeaNewTabInExistingOrNewWindow:
795           begin
796             FWindowIndex := AvailSrcWindowIndex(AnUnitInfo);
797             if FWindowIndex < 0 then
798               FWindowIndex := SourceEditorManager.SourceWindowCount;
799             if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
800               Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
801           end;
802       end;
803     if Result <> nil then
804       break;
805   end;
806   if Result = nil then
807     // should never happen
808     Result := AnUnitInfo.OpenEditorInfo[0];
809   if Result<>nil then begin
810     // WantedTopLine
811     if (WantedTopLine>0)
812     and (Result.EditorComponent<>nil) then
813       Result.EditorComponent.TopLine:=WantedTopLine;
814   end;
815 end;
816 
TFileOpener.OpenResourcenull817 function TFileOpener.OpenResource: TModalResult;
818 var
819   CloseFlags: TCloseFlags;
820 begin
821   // read form data
822   if FilenameIsPascalUnit(FFilename) then begin
823     // this could be a unit with a form
824     //debugln('TFileOpener.OpenResource ',FFilename,' ',OpenFlagsToString(Flags));
825     if ([ofDoNotLoadResource]*FFlags=[])
826     and ( (ofDoLoadResource in FFlags)
827        or ((ofProjectLoading in FFlags)
828            and FNewUnitInfo.LoadedDesigner
829            and (not Project1.AutoOpenDesignerFormsDisabled)
830            and EnvironmentOptions.AutoCreateFormsOnOpen))
831     then begin
832       // -> try to (re)load the lfm file
833       //debugln(['TFileOpener.OpenResource Loading LFM for ',FNewUnitInfo.Filename,' LoadedDesigner=',FNewUnitInfo.LoadedDesigner]);
834       CloseFlags:=[cfSaveDependencies];
835       if ofRevert in FFlags then
836         Include(CloseFlags,cfCloseDependencies);
837       Result:=LoadLFM(FNewUnitInfo,FFlags,CloseFlags);
838       if Result<>mrOk then begin
839         DebugLn(['TFileOpener.OpenResource LoadLFM failed']);
840         exit;
841       end;
842     end else begin
843       Result:=mrOk;
844     end;
845   end else if FNewUnitInfo.Component<>nil then begin
846     // this is no pascal source and there is a designer form
847     // This can be the case, when the file is renamed and/or reverted
848     // -> close form
849     Result:=CloseUnitComponent(FNewUnitInfo,[cfCloseDependencies,cfSaveDependencies]);
850     if Result<>mrOk then begin
851       DebugLn(['TFileOpener.OpenResource CloseUnitComponent failed']);
852     end;
853   end else begin
854     Result:=mrOk;
855   end;
856   if FNewUnitInfo.Component=nil then
857     FNewUnitInfo.LoadedDesigner:=false;
858 end;
859 
860 procedure TFileOpener.CheckInternalFile;
861 var
862   NewBuf: TCodeBuffer;
863 begin
864   if (copy(FFileName, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive)
865   then begin
866     FUnitIndex:=Project1.IndexOfFilename(FFilename);
867     if (FUnitIndex < 0) then begin
868       NewBuf := CodeToolBoss.SourceCache.CreateFile(FFileName);
869       if MacroListViewer.MacroByFullName(FFileName) <> nil then
870         NewBuf.Source := MacroListViewer.MacroByFullName(FFileName).GetAsSource;
871       FNewUnitInfo:=TUnitInfo.Create(NewBuf);
872       FNewUnitInfo.DefaultSyntaxHighlighter := lshFreePascal;
873       Project1.AddFile(FNewUnitInfo,false);
874     end
875     else begin
876       FNewUnitInfo:=Project1.Units[FUnitIndex];
877     end;
878     FNewUnitInfo.Flags := FNewUnitInfo.Flags + [uifInternalFile];
879 
880     if FNewUnitInfo.OpenEditorInfoCount > 0 then begin
881       FNewEditorInfo := FNewUnitInfo.OpenEditorInfo[0];
882       SourceEditorManager.SetWindowByIDAndPage(FNewEditorInfo.WindowID, FNewEditorInfo.PageIndex);
883     end
884     else begin
885       FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo;
886       OpenFileInSourceEditor(FNewEditorInfo);
887     end;
888   end;
889 end;
890 
TFileOpener.CheckRevertnull891 function TFileOpener.CheckRevert: TModalResult;
892 // revert: use source editor filename
893 begin
894   if (FPageIndex>=0) then begin
895     if FUseWindowID then                       // Revert must have a valid ID
896       FWindowIndex := SourceEditorManager.IndexOfSourceWindowWithID(FWindowIndex);
897     FUseWindowID := False;
898     Assert((FWindowIndex >= 0) and (FWindowIndex < SourceEditorManager.SourceWindowCount), 'FWindowIndex for revert');
899     FFilename := SourceEditorManager.SourceEditorsByPage[FWindowIndex, FPageIndex].FileName;
900   end
901   else
902     FFlags := FFlags - [ofRevert];    // No editor exists yet, don't try to revert.
903   FUnitIndex:=Project1.IndexOfFilename(FFilename);
904   if (FUnitIndex > 0) then begin
905     FNewUnitInfo:=Project1.Units[FUnitIndex];
906     if (uifInternalFile in FNewUnitInfo.Flags) then
907     begin
908       if (FNewUnitInfo.OpenEditorInfoCount > 0) then begin
909         FNewEditorInfo := FNewUnitInfo.OpenEditorInfo[0];
910         if MacroListViewer.MacroByFullName(FFileName) <> nil then
911           FNewUnitInfo.Source.Source := MacroListViewer.MacroByFullName(FFileName).GetAsSource;
912         FUseWindowID:=True;
913         FPageIndex := FNewEditorInfo.PageIndex;
914         FWindowIndex := FNewEditorInfo.WindowID;
915         OpenFileInSourceEditor(FNewEditorInfo);
916       end;
917       // else unknown internal file
918       exit(mrIgnore);
919     end;
920   end;
921   exit(mrOk);
922 end;
923 
PrepareRevertnull924 function TFileOpener.PrepareRevert(DiskFilename: String): TModalResult;
925 var
926   WInd: integer;
927   ed: TSourceEditor;
928 begin
929   FUnknownFile := False;
930   if FUseWindowID then
931     WInd:=SourceEditorManager.IndexOfSourceWindowWithID(FWindowIndex)
932   else
933     WInd:=FWindowIndex;
934   ed := SourceEditorManager.SourceEditorsByPage[WInd, FPageIndex];
935   FNewEditorInfo := Project1.EditorInfoWithEditorComponent(ed);
936   FNewUnitInfo := FNewEditorInfo.UnitInfo;
937   FUnitIndex:=Project1.IndexOf(FNewUnitInfo);
938   FFilename:=FNewUnitInfo.Filename;
939   if CompareFilenames(FFileName,DiskFilename)=0 then
940     FFileName:=DiskFilename;
941   if FNewUnitInfo.IsVirtual then begin
942     if (not (ofQuiet in FFlags)) then begin
943       IDEMessageDialog(lisRevertFailed, Format(lisFileIsVirtual, [FFilename]),
944         mtInformation,[mbCancel]);
945     end;
946     exit(mrCancel);
947   end;
948   exit(mrOK);
949 end;
950 
PrepareFilenull951 function TFileOpener.PrepareFile: TModalResult;
952 begin
953   FUnitIndex:=Project1.IndexOfFilename(FFilename);
954   FUnknownFile := (FUnitIndex < 0);
955   FNewEditorInfo := nil;
956   if not FUnknownFile then begin
957     FNewUnitInfo := Project1.Units[FUnitIndex];
958     if FEditorInfo <> nil then
959       FNewEditorInfo := FEditorInfo
960     else if (ofProjectLoading in FFlags) then
961       FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo
962     else
963       FNewEditorInfo := FNewUnitInfo.EditorInfo[0];
964   end;
965   Result := mrOK;
966 end;
967 
ChangeEditorPagenull968 function TFileOpener.ChangeEditorPage: TModalResult;
969 // file already open -> change source notebook page
970 begin
971   //DebugLn(['TFileOpener.ChangeEditorPage file already open ',FNewUnitInfo.Filename,' WindowIndex=',FNewEditorInfo.WindowID,' PageIndex=',FNewEditorInfo.PageIndex]);
972   SourceEditorManager.SetWindowByIDAndPage(FNewEditorInfo.WindowID, FNewEditorInfo.PageIndex);
973   if ofDoLoadResource in FFlags then
974     Result:=OpenResource
975   else
976     Result:=mrOk;
977 end;
978 
OpenKnownnull979 function TFileOpener.OpenKnown: TModalResult;
980 // project knows this file => all the meta data is known -> just load the source
981 var
982   LoadBufferFlags: TLoadBufferFlags;
983   NewBuf: TCodeBuffer;
984 begin
985   FNewUnitInfo:=Project1.Units[FUnitIndex];
986   LoadBufferFlags:=[lbfCheckIfText];
987   if FilenameIsAbsolute(FFilename) then begin
988     if (not (ofUseCache in FFlags)) then
989       Include(LoadBufferFlags,lbfUpdateFromDisk);
990     if ofRevert in FFlags then
991       Include(LoadBufferFlags,lbfRevert);
992   end;
993   Result:=LoadCodeBuffer(NewBuf,FFileName,LoadBufferFlags,
994                          [ofProjectLoading,ofMultiOpen]*FFlags<>[]);
995   if Result<>mrOk then begin
996     DebugLn(['TFileOpener.OpenKnownFile failed LoadCodeBuffer: ',FFilename]);
997     exit;
998   end;
999   FNewUnitInfo.Source:=NewBuf;
1000   if FilenameIsPascalUnit(FNewUnitInfo.Filename) then
1001     FNewUnitInfo.ReadUnitNameFromSource(false);
1002   FNewUnitInfo.Modified:=FNewUnitInfo.Source.FileOnDiskNeedsUpdate;
1003 end;
1004 
OpenUnknownnull1005 function TFileOpener.OpenUnknown: TModalResult;
1006 // open unknown file, Never happens if ofRevert
1007 begin
1008   Result:=OpenUnknownFile;
1009   if Result<>mrOk then exit;
1010   // the file was previously unknown, use the default EditorInfo
1011   if FEditorInfo <> nil then
1012     FNewEditorInfo := FEditorInfo
1013   else
1014   if FNewUnitInfo <> nil then
1015     FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo
1016   else
1017     FNewEditorInfo := nil;
1018 end;
1019 
OpenUnknownFilenull1020 function TFileOpener.OpenUnknownFile: TModalResult;
1021 var
1022   Ext, NewProgramName, LPIFilename, ACaption, AText: string;
1023   PreReadBuf: TCodeBuffer;
1024   LoadFlags: TLoadBufferFlags;
1025   SourceType: String;
1026 begin
1027   Ext:=lowercase(ExtractFileExt(FFilename));
1028 
1029   if ([ofProjectLoading,ofRegularFile]*FFlags=[]) and (MainIDE.ToolStatus=itNone)
1030   and (Ext='.lpi') then begin
1031     // this is a project info file -> load whole project
1032     Result:=MainIDE.DoOpenProjectFile(FFilename,[ofAddToRecent]);
1033     if Result = mrOK then
1034       Result := mrIgnore;
1035     exit;
1036   end;
1037 
1038   // load the source
1039   LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert];
1040   if ofQuiet in FFlags then Include(LoadFlags, lbfQuiet);
1041   Result:=LoadCodeBuffer(PreReadBuf,FFileName,LoadFlags,true);
1042   if Result<>mrOk then exit;
1043   FNewUnitInfo:=nil;
1044 
1045   // check if unit is a program
1046   if ([ofProjectLoading,ofRegularFile]*FFlags=[])
1047   and FilenameIsPascalSource(FFilename) then begin
1048     SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false);
1049     if (SysUtils.CompareText(SourceType,'PROGRAM')=0)
1050     or (SysUtils.CompareText(SourceType,'LIBRARY')=0)
1051     then begin
1052       NewProgramName:=CodeToolBoss.GetSourceName(PreReadBuf,false);
1053       if NewProgramName<>'' then begin
1054         // source is a program
1055         // either this is a lazarus project or it is not yet a lazarus project ;)
1056         LPIFilename:=ChangeFileExt(FFilename,'.lpi');
1057         if FileExistsCached(LPIFilename) then begin
1058           case IDEQuestionDialog(lisProjectInfoFileDetected,
1059             Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP,
1060                    [FFilename]), mtConfirmation,
1061               [mrOk, lisOpenProject2, mrAbort, lisOpenTheFileAsNormalSource])
1062           of
1063             mrOk:
1064             begin
1065               Result:=MainIDE.DoOpenProjectFile(LPIFilename,[ofAddToRecent]);
1066               if Result = mrOK then
1067                 Result := mrIgnore;
1068               exit;
1069             end;
1070             mrCancel: Exit(mrCancel);
1071           end;
1072         end else begin
1073           AText:=Format(lisTheFileSeemsToBeAProgramCloseCurrentProject,
1074                         [FFilename, LineEnding, LineEnding]);
1075           ACaption:=lisProgramDetected;
1076           if IDEMessageDialog(ACaption, AText, mtConfirmation, [mbYes,mbNo])=mrYes then
1077           begin
1078             Result:=CreateProjectForProgram(PreReadBuf);
1079             if Result = mrOK then
1080               Result := mrIgnore;
1081             exit;
1082           end;
1083         end;
1084       end;
1085     end;
1086   end;
1087   FNewUnitInfo:=TUnitInfo.Create(PreReadBuf);
1088   if FilenameIsPascalSource(FNewUnitInfo.Filename) then
1089     FNewUnitInfo.ReadUnitNameFromSource(true);
1090   Project1.AddFile(FNewUnitInfo,false);
1091   if (ofAddToProject in FFlags) and (not FNewUnitInfo.IsPartOfProject) then
1092   begin
1093     FNewUnitInfo.IsPartOfProject:=true;
1094     Project1.Modified:=true;
1095   end;
1096   Result:=mrOk;
1097 end;
1098 
OpenNotExistingFilenull1099 function TFileOpener.OpenNotExistingFile: TModalResult;
1100 var
1101   NewFlags: TNewFlags;
1102 begin
1103   if ofProjectLoading in FFlags then begin
1104     // this is a file that was loaded last time, but was removed from disk
1105     Result:=IDEQuestionDialog(lisFileNotFound,
1106       Format(lisTheFileWasNotFoundIgnoreWillGoOnLoadingTheProject,
1107              [FFilename, LineEnding, LineEnding]),
1108       mtError, [mrIgnore, lisSkipFileAndContinueLoading,
1109                 mrAbort, lisAbortLoadingProject]);
1110     exit;
1111   end;
1112 
1113   // Default to cancel
1114   Result:=mrCancel;
1115   if ofQuiet in FFlags then Exit;
1116 
1117   if ofOnlyIfExists in FFlags then
1118   begin
1119     IDEMessageDialog(lisFileNotFound,
1120       Format(lisFileNotFound2, [FFilename])+LineEnding, mtInformation,[mbCancel]);
1121     // cancel loading file
1122     Exit;
1123   end;
1124 
1125   if IDEMessageDialog(lisFileNotFound,
1126       Format(lisFileNotFoundDoYouWantToCreateIt,[FFilename,LineEnding]),
1127       mtInformation,[mbYes,mbNo])=mrYes then
1128   begin
1129     // create new file
1130     NewFlags:=[nfOpenInEditor,nfCreateDefaultSrc];
1131     if ofAddToProject in FFlags then
1132       Include(NewFlags,nfIsPartOfProject);
1133     if FilenameIsPascalSource(FFilename) then
1134       Result:=MainIDE.DoNewEditorFile(FileDescriptorUnit,FFilename,'',NewFlags)
1135     else
1136       Result:=MainIDE.DoNewEditorFile(FileDescriptorText,FFilename,'',NewFlags);
1137   end;
1138 end;
1139 
ResolvePossibleSymlinknull1140 function TFileOpener.ResolvePossibleSymlink: TModalResult;
1141 // Check if symlink and ask user if the real file should be opened instead.
1142 // Compiler never resolves symlinks, files in compiler search path must not be resolved.
1143 // If there already is an editor with a "physical" target of a symlink, use it.
1144 var
1145   SPath, Target: String;  // Search path and target file for the symlink.
1146 begin
1147   Result := mrOK;
1148   if ofProjectLoading in FFlags then Exit; // Use the given name when project loads.
1149   Target := GetPhysicalFilenameCached(FFileName,false);
1150   if Target = FFilename then Exit;  // Not a symlink, continue with FFilename.
1151   // ToDo: Check if there is an editor with a symlink for this "physical" file.
1152 
1153   SPath := CodeToolBoss.GetCompleteSrcPathForDirectory('');
1154   // Check if symlink is found in search path or in editor.
1155   if (SearchDirectoryInSearchPath(SPath, ExtractFilePath(FFileName)) > 0)
1156   or Assigned(SourceEditorManager.SourceEditorIntfWithFilename(FFileName))
1157   then
1158     Exit;       // Symlink found -> use it.
1159   // Check if "physical" target for a symlink is found in search path or in editor.
1160   if (SearchDirectoryInSearchPath(SPath, ExtractFilePath(Target)) > 0)
1161   or Assigned(SourceEditorManager.SourceEditorIntfWithFilename(Target))
1162   then          // Target found -> use Target name.
1163     FFileName := Target
1164   else          // Not found anywhere, ask user.
1165     Result := ChooseSymlink(FFileName, Target);
1166 end;
1167 
OpenEditorFilenull1168 function TFileOpener.OpenEditorFile(APageIndex, AWindowIndex: integer;
1169   AEditorInfo: TUnitEditorInfo; AFlags: TOpenFlags): TModalResult;
1170 var
1171   s, DiskFilename: String;
1172   Reverting: Boolean;
1173 begin
1174   {$IFDEF IDE_VERBOSE}
1175   DebugLn('');
1176   DebugLn(['*** TFileOpener.OpenEditorFile START "',AFilename,'" ',OpenFlagsToString(Flags),' Window=',WindowIndex,' Page=',PageIndex]);
1177   {$ENDIF}
1178   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TFileOpener.OpenEditorFile START');{$ENDIF}
1179   FPageIndex := APageIndex;
1180   FWindowIndex := AWindowIndex;
1181   FEditorInfo := AEditorInfo;
1182   FFlags := AFlags;
1183 
1184   Result:=mrCancel;
1185 
1186   // replace macros
1187   if ofConvertMacros in FFlags then begin
1188     if not GlobalMacroList.SubstituteStr(FFilename) then exit;
1189     FFilename:=ExpandFileNameUTF8(FFilename);
1190   end;
1191 
1192   if (ofRevert in FFlags) then begin
1193     Result := CheckRevert;
1194     if Result = mrIgnore then exit(mrOK);
1195     Assert(Result = mrOK);
1196   end;
1197 
1198   if (ofInternalFile in FFlags) then begin
1199     CheckInternalFile;
1200     // unknown internal file => ignore
1201     exit(mrOK);
1202   end;
1203 
1204   // normalize filename
1205   FFilename:=TrimFilename(FFilename);
1206   DiskFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(FFilename);
1207   if DiskFilename<>FFilename then begin
1208     // the case is different
1209     DebugLn(['TFileOpener.OpenEditorFile Fixing file name: ',FFilename,' -> ',DiskFilename]);
1210     FFilename:=DiskFilename;
1211   end;
1212   if not (ofRegularFile in FFlags) then begin
1213     DiskFilename:=GetShellLinkTarget(FFileName);
1214     if DiskFilename<>FFilename then begin
1215       // not regular file
1216       DebugLn(['TFileOpener.OpenEditorFile Fixing file name: ',FFilename,' -> ',DiskFilename]);
1217       FFilename:=DiskFilename;
1218     end;
1219   end;
1220 
1221   if FilenameIsAbsolute(FFileName) then begin
1222     Result := ResolvePossibleSymlink;
1223     if Result <> mrOK then exit;
1224   end;
1225 
1226   // check to not open directories
1227   s:=ExtractFilename(FFilename);
1228   if (s='') or (s='.') or (s='..') then
1229   begin
1230     DebugLn(['TFileOpener.OpenEditorFile ignoring special file: ',FFilename]);
1231     exit;
1232   end;
1233   if DirectoryExistsUTF8(FFileName) then begin
1234     debugln(['TFileOpener.OpenEditorFile skipping directory ',FFileName]);
1235     exit(mrCancel);
1236   end;
1237 
1238   if ([ofAddToRecent,ofRevert,ofVirtualFile]*FFlags=[ofAddToRecent])
1239   and (FFilename<>'') and FilenameIsAbsolute(FFilename) then
1240     EnvironmentOptions.AddToRecentOpenFiles(FFilename);
1241 
1242   // check if this is a hidden unit:
1243   // if this is the main unit, it is already
1244   // loaded and needs only to be shown in the sourceeditor/formeditor
1245   if (not (ofRevert in FFlags)) and (CompareFilenames(Project1.MainFilename,FFilename)=0)
1246   then begin
1247     Result:=OpenMainUnit;
1248     exit;
1249   end;
1250 
1251   // check for special files
1252   if ([ofRegularFile,ofRevert,ofProjectLoading]*FFlags=[])
1253   and FilenameIsAbsolute(FFilename) and FileExistsCached(FFilename) then begin
1254     // check if file is a lazarus project (.lpi)
1255     if (CompareFileExt(FFilename,'.lpi',false)=0) then
1256     begin
1257       case
1258         IDEQuestionDialog(lisOpenProject, Format(lisOpenTheProject, [FFilename]),
1259             mtConfirmation, [mrYes, lisOpenProject2,
1260                              mrNoToAll, lisOpenAsXmlFile,
1261                              mrCancel])
1262       of
1263         mrYes: begin
1264           Result:=MainIDE.DoOpenProjectFile(FFilename,[ofAddToRecent]);
1265           exit;
1266         end;
1267         mrNoToAll: include(FFlags, ofRegularFile);
1268         mrCancel: exit(mrCancel);
1269       end;
1270     end;
1271 
1272     // check if file is a lazarus package (.lpk)
1273     if (CompareFileExt(FFilename,'.lpk',false)=0) then
1274     begin
1275       case
1276         IDEQuestionDialog(lisOpenPackage,
1277             Format(lisOpenThePackage, [FFilename]),
1278             mtConfirmation, [mrYes, lisCompPalOpenPackage,
1279                              mrNoToAll, lisOpenAsXmlFile,
1280                              mrCancel])
1281       of
1282         mrYes: begin
1283           Result:=PkgBoss.DoOpenPackageFile(FFilename,[pofAddToRecent],
1284                                        [ofProjectLoading,ofMultiOpen]*FFlags<>[]);
1285           exit;
1286         end;
1287         mrCancel: exit(mrCancel);
1288       end;
1289     end;
1290   end;
1291 
1292   // check if the project knows this file
1293   if (ofRevert in FFlags) then begin
1294     Result := PrepareRevert(DiskFilename);
1295     if Result <> mrOK then exit;
1296   end else begin
1297     Result := PrepareFile;
1298     if Result <> mrOK then exit;
1299   end;
1300 
1301   if (FNewEditorInfo <> nil) and (ofAddToProject in FFlags) and (not FNewUnitInfo.IsPartOfProject) then
1302   begin
1303     FNewUnitInfo.IsPartOfProject:=true;
1304     Project1.Modified:=true;
1305   end;
1306 
1307   if (FNewEditorInfo <> nil) and (FFlags * [ofProjectLoading, ofRevert] = [])
1308   and (FNewEditorInfo.EditorComponent <> nil) then
1309   begin
1310     Result := ChangeEditorPage;
1311     exit;
1312   end;
1313 
1314   Reverting:=ofRevert in FFlags;
1315   if Reverting then
1316     Project1.BeginRevertUnit(FNewUnitInfo);
1317   try
1318 
1319     // check if file exists
1320     if FilenameIsAbsolute(FFilename) and (not FileExistsCached(FFilename)) then
1321     begin
1322       // file does not exist
1323       if (ofRevert in FFlags) then begin
1324         // PrepareRevert failed, due to missing file
1325         if not (ofQuiet in FFlags) then begin
1326           IDEMessageDialog(lisRevertFailed, Format(lisPkgMangFileNotFound, [FFilename]),
1327             mtError,[mbCancel]);
1328         end;
1329         Result:=mrCancel;
1330         exit;
1331       end else begin
1332         Result:=OpenNotExistingFile;
1333         exit;
1334       end;
1335     end;
1336 
1337     // load the source
1338     if FUnknownFile then
1339       Result := OpenUnknown
1340     else
1341       Result := OpenKnown;
1342     if Result=mrIgnore then exit(mrOK);
1343     if Result<>mrOk then exit;
1344 
1345     // check readonly
1346     FNewUnitInfo.FileReadOnly:=FileExistsCached(FNewUnitInfo.Filename)
1347                               and (not FileIsWritable(FNewUnitInfo.Filename));
1348     //debugln('[TFileOpener.OpenEditorFile] B');
1349     // open file in source notebook
1350     Result:=OpenFileInSourceEditor(FNewEditorInfo);
1351     if Result<>mrOk then begin
1352       DebugLn(['TFileOpener.OpenEditorFile failed OpenFileInSourceEditor: ',FFilename]);
1353       exit;
1354     end;
1355     // open resource component (designer, form, datamodule, ...)
1356     if FNewUnitInfo.OpenEditorInfoCount = 1 then
1357       Result:=OpenResource;
1358     if Result<>mrOk then begin
1359       DebugLn(['TFileOpener.OpenEditorFile failed OpenResource: ',FFilename]);
1360       exit;
1361     end;
1362   finally
1363     if Reverting then
1364       Project1.EndRevertUnit(FNewUnitInfo);
1365   end;
1366 
1367   Result:=mrOk;
1368   //debugln('TFileOpener.OpenEditorFile END "',FFilename,'"');
1369   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TFileOpener.OpenEditorFile END');{$ENDIF}
1370 end;
1371 
FindFilenull1372 function TFileOpener.FindFile(SearchPath: String): Boolean;
1373 //  Searches for FileName in SearchPath
1374 //  If FileName is not found, we'll check extensions pp and pas too
1375 //  Returns true if found. FFileName contains the full file+path in that case
1376 var TempFile,TempPath,CurPath: String;
1377     p,c: Integer;
1378     PasExt: TPascalExtType;
1379 
SetFileIfExistsnull1380   function SetFileIfExists(const Ext: String): Boolean;
1381   var
1382     FinalFile: String;
1383   begin
1384     FinalFile:=ExpandFileNameUTF8(CurPath+TempFile+Ext);
1385     Result:=FileExistsCached(FinalFile);
1386     if Result then
1387       FFileName:=FinalFile;
1388   end;
1389 
1390 begin
1391   if SearchPath='' then SearchPath:='.';
1392   Result:=true;
1393   TempPath:=SearchPath;
1394   while TempPath<>'' do begin
1395     p:=pos(';',TempPath);
1396     if p=0 then p:=length(TempPath)+1;
1397     CurPath:=copy(TempPath,1,p-1);
1398     Delete(TempPath,1,p);
1399     if CurPath='' then continue;
1400     CurPath:=AppendPathDelim(CurPath);
1401     if not FilenameIsAbsolute(CurPath) then begin
1402       if FActiveUnitInfo.IsVirtual then
1403         CurPath:=AppendPathDelim(Project1.Directory)+CurPath
1404       else
1405         CurPath:=AppendPathDelim(ExtractFilePath(FActiveUnitInfo.Filename))+CurPath;
1406     end;
1407     for c:=0 to 2 do begin
1408       TempFile:='';
1409       // FPC searches first lowercase, then keeping case, then uppercase
1410       case c of
1411         0: TempFile:=LowerCase(FFileName);
1412         1: TempFile:=FFileName;
1413         2: TempFile:=UpperCase(FFileName);
1414       end;
1415       if ExtractFileExt(TempFile)='' then begin
1416         for PasExt:=Low(TPascalExtType) to High(TPascalExtType) do
1417           if SetFileIfExists(PascalExtension[PasExt]) then exit;
1418       end
1419       else
1420         if SetFileIfExists('') then exit;
1421     end;
1422   end;
1423   Result:=false;
1424 end;
1425 
CheckIfIncludeDirectiveInFrontnull1426 function TFileOpener.CheckIfIncludeDirectiveInFront(const Line: string;
1427   X: integer): boolean;
1428 var
1429   DirectiveEnd, DirectiveStart: integer;
1430   Directive: string;
1431 begin
1432   Result:=false;
1433   DirectiveEnd:=X;
1434   while (DirectiveEnd>1) and (Line[DirectiveEnd-1] in [' ',#9]) do
1435     dec(DirectiveEnd);
1436   DirectiveStart:=DirectiveEnd-1;
1437   while (DirectiveStart>0) and (Line[DirectiveStart]<>'$') do
1438     dec(DirectiveStart);
1439   Directive:=uppercase(copy(Line,DirectiveStart,DirectiveEnd-DirectiveStart));
1440   if (Directive='$INCLUDE') or (Directive='$I') then begin
1441     if ((DirectiveStart>1) and (Line[DirectiveStart-1]='{'))
1442     or ((DirectiveStart>2)
1443       and (Line[DirectiveStart-2]='(') and (Line[DirectiveStart-1]='*'))
1444     then begin
1445       Result:=true;
1446     end;
1447   end;
1448 end;
1449 
TFileOpener.GetFilenameAtRowColnull1450 function TFileOpener.GetFilenameAtRowCol(XY: TPoint): string;
1451 var
1452   Line: string;
1453   Len, Stop: integer;
1454   StopChars: set of char;
1455 begin
1456   Result := '';
1457   FIsIncludeDirective:=false;
1458   if (XY.Y >= 1) and (XY.Y <= FActiveSrcEdit.EditorComponent.Lines.Count) then
1459   begin
1460     Line := FActiveSrcEdit.EditorComponent.Lines.Strings[XY.Y - 1];
1461     Len := Length(Line);
1462     if (XY.X >= 1) and (XY.X <= Len + 1) then begin
1463       StopChars := [',',';',':','[',']','{','}','(',')','''','"','`'
1464                    ,'#','%','=','>'];
1465       Stop := XY.X;
1466       if Stop>Len then Stop:=Len;
1467       while (Stop >= 1) and (not (Line[Stop] in ['''','"','`'])) do
1468         dec(Stop);
1469       if Stop<1 then
1470         StopChars:=StopChars+[' ',#9]; // no quotes in front => use spaces as boundaries
1471       Stop := XY.X;
1472       while (Stop <= Len) and (not (Line[Stop] in StopChars)) do
1473         Inc(Stop);
1474       while (XY.X > 1) and (not (Line[XY.X - 1] in StopChars)) do
1475         Dec(XY.X);
1476       if Stop > XY.X then begin
1477         Result := Copy(Line, XY.X, Stop - XY.X);
1478         FIsIncludeDirective:=CheckIfIncludeDirectiveInFront(Line,XY.X);
1479       end;
1480     end;
1481   end;
1482 end;
1483 
TFileOpener.OpenFileAtCursornull1484 function TFileOpener.OpenFileAtCursor: TModalResult;
1485 
ShowNotFoundnull1486   function ShowNotFound(aFilename: string): TModalResult;
1487   begin
1488     Result:=mrCancel;
1489     if aFilename<>'' then
1490       IDEMessageDialog(lisOpenFileAtCursor, lisFileNotFound+':'#13+aFileName,
1491         mtError, [mbOk]);
1492   end;
1493 
1494 var
1495   Found: Boolean;
1496   BaseDir: String;
1497   NewFilename,InFilename: string;
1498   AUnitName: String;
1499   SearchPath, Line: String;
1500   Edit: TIDESynEditor;
1501   FoundType: TFindFileAtCursorFlag;
1502   XY: TPoint;
1503   Len: Integer;
1504 begin
1505   Result:=mrCancel;
1506 
1507   {$IFDEF VerboseFindFileAtCursor}
1508   debugln(['TFileOpener.OpenFileAtCursor ',FActiveUnitInfo<>nil]);
1509   {$ENDIF}
1510   if (FActiveSrcEdit=nil) or (FActiveUnitInfo=nil) then exit;
1511   BaseDir:=ExtractFilePath(FActiveUnitInfo.Filename);
1512   {$IFDEF VerboseFindFileAtCursor}
1513   debugln(['TFileOpener.OpenFileAtCursor File="',FActiveUnitInfo.Filename,'"']);
1514   {$ENDIF}
1515 
1516   Found:=false;
1517 
1518   // check if a filename is selected
1519   Edit:=FActiveSrcEdit.EditorComponent;
1520   if Edit.SelAvail and (Edit.BlockBegin.Y=Edit.BlockBegin.X) then begin
1521     {$IFDEF VerboseFindFileAtCursor}
1522     debugln(['TFileOpener.OpenFileAtCursor Edit.SelAvail Edit.SelText="',Edit.SelText,'"']);
1523     {$ENDIF}
1524     FFileName:=ResolveDots(Edit.SelText);
1525     if not FilenameIsAbsolute(FFileName) then
1526       FFileName:=ResolveDots(BaseDir+FFileName);
1527     if FilenameIsAbsolute(FFileName) then begin
1528       if FileExistsCached(FFileName) then
1529         Found:=true
1530       else
1531         exit(ShowNotFound(FFileName));
1532     end;
1533   end;
1534 
1535 
1536   XY:=Edit.LogicalCaretXY;
1537   if (XY.Y >= 1) and (XY.Y <= FActiveSrcEdit.EditorComponent.Lines.Count) then
1538   begin
1539     Line := FActiveSrcEdit.EditorComponent.Lines.Strings[XY.Y - 1];
1540     Len := Length(Line);
1541     if (XY.X>1) and (XY.X-1<=Len) and IsWordChar[Line[XY.X-1]]
1542     and ((XY.X>Len) or IsNonWordChar[Line[XY.X]]) then
1543       dec(XY.X);
1544   end;
1545 
1546 
1547   // in a Pascal file use codetools
1548   if FilenameIsPascalSource(FActiveUnitInfo.Filename) then begin
1549     {$IFDEF VerboseFindFileAtCursor}
1550     debugln(['TFileOpener.OpenFileAtCursor FilenameIsPascalSource -> using codetools']);
1551     {$ENDIF}
1552     if MainIDE.BeginCodeTool(FActiveSrcEdit,FActiveUnitInfo,[]) then begin
1553       if CodeToolBoss.FindFileAtCursor(FActiveSrcEdit.CodeBuffer,
1554         XY.X,XY.Y,FoundType,FFileName) then
1555         Found:=true
1556       else begin
1557         FFileName:=FActiveSrcEdit.EditorComponent.GetWordAtRowCol(
1558           FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1559         exit(ShowNotFound(FFileName));
1560       end;
1561     end;
1562   end;
1563 
1564   if not Found then begin
1565     // parse FFileName at cursor
1566     FFileName:=GetFilenameAtRowCol(FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1567     if FFileName='' then exit;
1568     // check if absolute FFileName
1569     if FilenameIsAbsolute(FFileName) then begin
1570       if FileExistsCached(FFileName) then
1571         Found:=true
1572       else
1573         exit(ShowNotFound(FFileName));
1574     end;
1575 
1576     if FIsIncludeDirective then
1577     begin
1578       if (not Found) then begin
1579         // search include file
1580         SearchPath:='.;'+CodeToolBoss.DefineTree.GetIncludePathForDirectory(BaseDir);
1581         if FindFile(SearchPath) then // sets FFileName if result=true
1582           Found:=true;
1583       end;
1584     end else
1585     begin
1586       if (not Found) then
1587       begin
1588         // search pascal unit without extension
1589         AUnitName:=FFileName;
1590         InFilename:='';
1591         NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1592                              BaseDir,AUnitName,InFilename,true);
1593         if NewFilename<>'' then begin
1594           Found:=true;
1595           FFileName:=NewFilename;
1596         end;
1597       end;
1598 
1599       if (not Found) and (ExtractFileExt(FFileName)<>'') then
1600       begin
1601         // search pascal unit with extension
1602         AUnitName:=ExtractFileNameOnly(FFileName);
1603         InFilename:=FFileName;
1604         NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1605                              BaseDir,AUnitName,InFilename,true);
1606         if NewFilename<>'' then begin
1607           Found:=true;
1608           FFileName:=NewFilename;
1609         end;
1610       end;
1611     end;
1612   end;
1613 
1614   if (not Found) then begin
1615     // simple search relative to current unit
1616     InFilename:=AppendPathDelim(BaseDir)+FFileName;
1617     if FileExistsCached(InFilename) then begin
1618       Found:=true;
1619       FFileName:=InFilename;
1620     end;
1621   end;
1622 
1623   if (not Found) and (System.Pos('.',FFileName)>0) and (not FIsIncludeDirective) then
1624   begin
1625     // for example 'SysUtils.CompareText'
1626     FFileName:=FActiveSrcEdit.EditorComponent.GetWordAtRowCol(
1627       FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1628     if IsValidIdent(FFileName) then begin
1629       // search pascal unit
1630       AUnitName:=FFileName;
1631       InFilename:='';
1632       NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1633                            BaseDir,AUnitName,InFilename,true);
1634       if NewFilename<>'' then begin
1635         Found:=true;
1636         FFileName:=NewFilename;
1637       end;
1638     end;
1639   end;
1640 
1641   if Found then begin
1642     // open, FFileName is set earlier.
1643     InputHistories.SetFileDialogSettingsInitialDir(ExtractFilePath(FFileName));
1644     FUseWindowID:=False;
1645     Result:=OpenEditorFile(-1, -1, nil, [ofAddToRecent]);
1646   end else
1647     exit(ShowNotFound(FFileName));
1648 end;
1649 
OpenMainUnitnull1650 function TFileOpener.OpenMainUnit: TModalResult;
1651 var
1652   MainUnitInfo: TUnitInfo;
1653 begin
1654   {$IFDEF IDE_VERBOSE}
1655   debugln(['[TFileOpener.OpenMainUnit] A ProjectLoading=',ofProjectLoading in Flags,' MainUnitID=',Project1.MainUnitID]);
1656   {$ENDIF}
1657   Result:=mrCancel;
1658   if (Project1=nil) or (Project1.MainUnitID<0) then exit;
1659   MainUnitInfo:=Project1.MainUnitInfo;
1660 
1661   // check if main unit is already open in source editor
1662   if (MainUnitInfo.OpenEditorInfoCount > 0) and (not (ofProjectLoading in FFlags)) then
1663   begin
1664     // already loaded -> switch to source editor
1665     SourceEditorManager.ActiveEditor := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent);
1666     SourceEditorManager.ShowActiveWindowOnTop(True);
1667     Result:=mrOk;
1668     exit;
1669   end;
1670 
1671   // open file in source notebook
1672   Result:=OpenFileInSourceEditor(MainUnitInfo.GetClosedOrNewEditorInfo);
1673   if Result<>mrOk then exit;
1674 
1675   Result:=mrOk;
1676   {$IFDEF IDE_VERBOSE}
1677   debugln('[TFileOpener.OpenMainUnit] END');
1678   {$ENDIF}
1679 end;
1680 
RevertMainUnitnull1681 function TFileOpener.RevertMainUnit: TModalResult;
1682 begin
1683   Result:=mrOk;
1684   if Project1.MainUnitID<0 then exit;
1685   FFileName:='';
1686   FUseWindowID:=True;
1687   if Project1.MainUnitInfo.OpenEditorInfoCount > 0 then
1688     // main unit is loaded, so we can just revert
1689     Result:=OpenEditorFile(Project1.MainUnitInfo.EditorInfo[0].PageIndex,
1690                            Project1.MainUnitInfo.EditorInfo[0].WindowID, nil, [ofRevert])
1691   else begin
1692     // main unit is only loaded in background
1693     // -> just reload the source and update the source name
1694     Result:=Project1.MainUnitInfo.ReadUnitSource(true,true);
1695   end;
1696 end;
1697 
CheckMainSrcLCLInterfacesnull1698 function CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult;
1699 var
1700   MainUnitInfo: TUnitInfo;
1701   MainUsesSection,ImplementationUsesSection: TStrings;
1702   MsgResult: TModalResult;
1703 begin
1704   Result:=mrOk;
1705   if (Project1=nil) then exit;
1706   if Project1.SkipCheckLCLInterfaces then exit;
1707   MainUnitInfo:=Project1.MainUnitInfo;
1708   if (MainUnitInfo=nil) or (MainUnitInfo.Source=nil) then exit;
1709   if PackageGraph.FindDependencyRecursively(Project1.FirstRequiredDependency,
1710     PackageGraph.LCLBasePackage)=nil
1711   then
1712     exit; // project does not use LCLBase
1713   // project uses LCLBase
1714   MainUsesSection:=nil;
1715   ImplementationUsesSection:=nil;
1716   try
1717     if not CodeToolBoss.FindUsedUnitNames(MainUnitInfo.Source,
1718       MainUsesSection,ImplementationUsesSection) then exit;
1719     if (SearchInStringListI(MainUsesSection,'forms')<0)
1720     and (SearchInStringListI(ImplementationUsesSection,'forms')<0) then
1721       exit;
1722     // project uses lcl unit Forms
1723     if (SearchInStringListI(MainUsesSection,'interfaces')>=0)
1724     or (SearchInStringListI(ImplementationUsesSection,'interfaces')>=0) then
1725       exit;
1726     // project uses lcl unit Forms, but not unit interfaces
1727     // this will result in strange linker error
1728     if not Silent then
1729     begin
1730       MsgResult:=IDEQuestionDialog(lisCCOWarningCaption,
1731         Format(lisTheProjectDoesNotUseTheLCLUnitInterfacesButItSeems, [LineEnding]),
1732         mtWarning, [mrYes, lisAddUnitInterfaces,
1733                     mrNo, lisIgnore,
1734                     mrNoToAll, lisAlwaysIgnore,
1735                     mrCancel]);
1736       case MsgResult of
1737         mrNo: exit;
1738         mrNoToAll: begin Project1.SkipCheckLCLInterfaces:=true; exit; end;
1739         mrCancel: exit(mrCancel);
1740       end;
1741     end;
1742     CodeToolBoss.AddUnitToMainUsesSection(MainUnitInfo.Source,'Interfaces','');
1743   finally
1744     MainUsesSection.Free;
1745     ImplementationUsesSection.Free;
1746   end;
1747 end;
1748 
1749 procedure AddRecentProjectFile(const AFilename: string);
1750 begin
1751   EnvironmentOptions.AddToRecentProjectFiles(AFilename);
1752   MainIDE.SetRecentProjectFilesMenu;
1753   MainIDE.SaveEnvironment;
1754 end;
1755 
1756 procedure RemoveRecentProjectFile(const AFilename: string);
1757 begin
1758   EnvironmentOptions.RemoveFromRecentProjectFiles(AFilename);
1759   MainIDE.SetRecentProjectFilesMenu;
1760   MainIDE.SaveEnvironment;
1761 end;
1762 
AddUnitToProjectnull1763 function AddUnitToProject(const AEditor: TSourceEditorInterface): TModalResult;
1764 var
1765   ActiveSourceEditor: TSourceEditor;
1766   ActiveUnitInfo: TUnitInfo;
1767   s, ShortUnitName, LFMFilename, LFMType, LFMComponentName,
1768     LFMClassName: string;
1769   OkToAdd: boolean;
1770   Owners: TFPList;
1771   i: Integer;
1772   APackage: TLazPackage;
1773   MsgResult: TModalResult;
1774   LFMCode: TCodeBuffer;
1775 begin
1776   Result:=mrCancel;
1777   if AEditor<>nil then
1778   begin
1779     ActiveSourceEditor := AEditor as TSourceEditor;
1780     if not MainIDE.BeginCodeTool(ActiveSourceEditor,ActiveUnitInfo,[ctfUseGivenSourceEditor]) then exit;
1781   end else
1782   begin
1783     ActiveSourceEditor:=nil;
1784     if not MainIDE.BeginCodeTool(ActiveSourceEditor,ActiveUnitInfo,[]) then exit;
1785   end;
1786   if (ActiveUnitInfo=nil) then exit;
1787   if ActiveUnitInfo.IsPartOfProject then begin
1788     if not ActiveUnitInfo.IsVirtual then
1789       s:=Format(lisTheFile, [ActiveUnitInfo.Filename])
1790     else
1791       s:=Format(lisTheFile, [ActiveSourceEditor.PageName]);
1792     s:=Format(lisisAlreadyPartOfTheProject, [s]);
1793     IDEMessageDialog(lisInformation, s, mtInformation, [mbOk]);
1794     exit;
1795   end;
1796   if not ActiveUnitInfo.IsVirtual then
1797     s:='"'+ActiveUnitInfo.Filename+'"'
1798   else
1799     s:='"'+ActiveSourceEditor.PageName+'"';
1800   if (ActiveUnitInfo.Unit_Name<>'')
1801   and (Project1.IndexOfUnitWithName(ActiveUnitInfo.Unit_Name,true,ActiveUnitInfo)>=0) then
1802   begin
1803     IDEMessageDialog(lisInformation, Format(
1804       lisUnableToAddToProjectBecauseThereIsAlreadyAUnitWith, [s]),
1805       mtInformation, [mbOk]);
1806     exit;
1807   end;
1808 
1809   Owners:=PkgBoss.GetPossibleOwnersOfUnit(ActiveUnitInfo.Filename,[]);
1810   try
1811     if (Owners<>nil) then begin
1812       for i:=0 to Owners.Count-1 do begin
1813         if TObject(Owners[i]) is TLazPackage then begin
1814           APackage:=TLazPackage(Owners[i]);
1815           MsgResult:=IDEQuestionDialog(lisAddPackageRequirement,
1816             Format(lisTheUnitBelongsToPackage, [APackage.IDAsString]),
1817             mtConfirmation, [mrYes, lisAddPackageToProject2,
1818                              mrIgnore, lisAddUnitNotRecommended,
1819                              mrCancel],'');
1820           case MsgResult of
1821             mrYes:
1822               begin
1823                 PkgBoss.AddProjectDependency(Project1,APackage);
1824                 exit;
1825               end;
1826             mrIgnore: ;
1827           else
1828             exit;
1829           end;
1830         end;
1831       end;
1832     end;
1833   finally
1834     Owners.Free;
1835   end;
1836 
1837   if FilenameIsPascalUnit(ActiveUnitInfo.Filename)
1838   and (EnvironmentOptions.CharcaseFileAction<>ccfaIgnore) then
1839   begin
1840     // ask user to apply naming conventions
1841     Result:=RenameUnitLowerCase(ActiveUnitInfo,true);
1842     if Result=mrIgnore then Result:=mrOk;
1843     if Result<>mrOk then begin
1844       DebugLn('AddActiveUnitToProject A RenameUnitLowerCase failed ',ActiveUnitInfo.Filename);
1845       exit;
1846     end;
1847   end;
1848 
1849   if IDEMessageDialog(lisConfirmation, Format(lisAddToProject, [s]),
1850     mtConfirmation, [mbYes, mbCancel]) in [mrOk, mrYes]
1851   then begin
1852     OkToAdd:=True;
1853     if FilenameIsPascalUnit(ActiveUnitInfo.Filename) then
1854       OkToAdd:=CheckDirIsInSearchPath(ActiveUnitInfo,False)
1855     else if CompareFileExt(ActiveUnitInfo.Filename,'inc',false)=0 then
1856       OkToAdd:=CheckDirIsInSearchPath(ActiveUnitInfo,True);
1857     if OkToAdd then begin
1858       ActiveUnitInfo.IsPartOfProject:=true;
1859       Project1.Modified:=true;
1860       if (FilenameIsPascalUnit(ActiveUnitInfo.Filename))
1861       and (pfMainUnitHasUsesSectionForAllUnits in Project1.Flags)
1862       then begin
1863         ActiveUnitInfo.ReadUnitNameFromSource(false);
1864         ShortUnitName:=ActiveUnitInfo.CreateUnitName;
1865         if (ShortUnitName<>'') then begin
1866           if CodeToolBoss.AddUnitToMainUsesSection(Project1.MainUnitInfo.Source,ShortUnitName,'')
1867           then
1868             Project1.MainUnitInfo.Modified:=true;
1869         end;
1870       end;
1871     end;
1872   end;
1873 
1874   if Project1.AutoCreateForms
1875   and (pfMainUnitHasCreateFormStatements in Project1.Flags)
1876   and FilenameIsPascalUnit(ActiveUnitInfo.Filename) then
1877   begin
1878     UpdateUnitInfoResourceBaseClass(ActiveUnitInfo,true);
1879     if ActiveUnitInfo.ResourceBaseClass in [pfcbcForm,pfcbcCustomForm,pfcbcDataModule] then
1880     begin
1881       LFMFilename:=ActiveUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(ActiveUnitInfo.Filename,true);
1882       if LoadCodeBuffer(LFMCode,LFMFilename,[lbfUpdateFromDisk],false)=mrOk then
1883       begin
1884         // read lfm header
1885         ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
1886         if (LFMComponentName<>'')
1887         and (LFMClassName<>'') then begin
1888           if IDEMessageDialog(lisAddToStartupComponents,
1889             Format(lisShouldTheComponentBeAutoCreatedWhenTheApplicationS, [
1890               LFMComponentName]),
1891             mtInformation,[mbYes,mbNo])=mrYes then
1892           begin
1893             Project1.AddCreateFormToProjectFile(LFMClassName,LFMComponentName);
1894           end;
1895         end;
1896       end;
1897     end;
1898   end;
1899 end;
1900 
1901 procedure UpdateSourceNames;
1902 var
1903   i: integer;
1904   AnUnitInfo: TUnitInfo;
1905   SourceName, PageName: string;
1906   AEditor: TSourceEditor;
1907 begin
1908   for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
1909     AEditor := SourceEditorManager.SourceEditors[i];
1910     AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
1911     if AnUnitInfo=nil then continue;
1912     if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin
1913       SourceName:=CodeToolBoss.GetCachedSourceName(AnUnitInfo.Source);
1914       if SourceName<>'' then
1915         AnUnitInfo.ReadUnitNameFromSource(true);
1916     end else
1917       SourceName:='';
1918     PageName:=CreateSrcEditPageName(SourceName, AnUnitInfo.Filename, AEditor);
1919     AEditor.PageName := PageName;
1920   end;
1921 end;
1922 
CheckEditorNeedsSavenull1923 function CheckEditorNeedsSave(AEditor: TSourceEditorInterface;
1924   IgnoreSharedEdits: Boolean): Boolean;
1925 var
1926   AnEditorInfo: TUnitEditorInfo;
1927   AnUnitInfo: TUnitInfo;
1928 begin
1929   Result := False;
1930   if AEditor = nil then exit;
1931   AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor);
1932   if AnEditorInfo = nil then exit;
1933 
1934   AnUnitInfo := AnEditorInfo.UnitInfo;
1935   if (AnUnitInfo.OpenEditorInfoCount > 1) and IgnoreSharedEdits then
1936     exit;
1937 
1938   // save some meta data of the source
1939   SaveSrcEditorProjectSpecificSettings(AnEditorInfo);
1940 
1941   Result := (AEditor.Modified) or (AnUnitInfo.Modified);
1942 end;
1943 
1944 procedure ArrangeSourceEditorAndMessageView(PutOnTop: boolean);
1945 begin
1946   if SourceEditorManager.SourceWindowCount > 0 then
1947   begin
1948     if PutOnTop then
1949     begin
1950       IDEWindowCreators.ShowForm(MessagesView,true);
1951       SourceEditorManager.ShowActiveWindowOnTop(False);
1952       exit;
1953     end;
1954   end;
1955   MainIDE.DoShowMessagesView(PutOnTop);
1956 end;
1957 
SomethingOfProjectIsModifiednull1958 function SomethingOfProjectIsModified(Verbose: boolean): boolean;
1959 begin
1960   Result:=(Project1<>nil)
1961       and (Project1.SomethingModified(true,true,Verbose)
1962            or SourceEditorManager.SomethingModified(Verbose));
1963 end;
1964 
FileExistsInIDEnull1965 function FileExistsInIDE(const Filename: string;
1966   SearchFlags: TProjectFileSearchFlags): boolean;
1967 begin
1968   Result:=FileExistsCached(Filename)
1969           or ((Project1<>nil) and (Project1.UnitInfoWithFilename(Filename,SearchFlags)<>nil));
1970 end;
1971 
BeautifySrcnull1972 function BeautifySrc(const s: string): string;
1973 begin
1974   Result:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(s,0);
1975 end;
1976 
NewFilenull1977 function NewFile(NewFileDescriptor: TProjectFileDescriptor;
1978   var NewFilename: string; NewSource: string;
1979   NewFlags: TNewFlags; NewOwner: TObject): TModalResult;
1980 var
1981   NewUnitInfo: TUnitInfo;
1982   NewSrcEdit: TSourceEditor;
1983   NewUnitName: string;
1984   NewBuffer: TCodeBuffer;
1985   OldUnitIndex: Integer;
1986   AncestorType: TPersistentClass;
1987   LFMFilename: String;
1988   SearchFlags: TProjectFileSearchFlags;
1989   LFMSourceText: String;
1990   LFMCode: TCodeBuffer;
1991   AProject: TProject;
1992   LRSFilename: String;
1993   ResType: TResourceType;
1994   SrcNoteBook: TSourceNotebook;
1995   AShareEditor: TSourceEditor;
1996   DisableAutoSize: Boolean;
1997   APackage: TLazPackage;
1998   IsPartOfProject: Boolean;
1999   RequiredPackages: String;
2000   Src: String;
2001   i: Integer;
2002   LFindDesignerBaseClassByName: Boolean = True;
2003   PreventAutoSize: Boolean;
2004 begin
2005   //debugln('NewFile A NewFilename=',NewFilename);
2006   // empty NewFilename is ok, it will be auto generated
2007   SaveEditorChangesToCodeCache(nil);
2008 
2009   // convert macros in filename
2010   if nfConvertMacros in NewFlags then begin
2011     if not GlobalMacroList.SubstituteStr(NewFilename) then begin
2012       Result:=mrCancel;
2013       exit;
2014     end;
2015   end;
2016 
2017   Result:=NewFileDescriptor.Init(NewFilename,NewOwner,NewSource,nfQuiet in NewFlags);
2018   if Result<>mrOk then exit;
2019 
2020   if FilenameIsAbsolute(NewFilename) and DirectoryExistsUTF8(NewFilename) then
2021   begin
2022     IDEMessageDialog(lisFileIsDirectory,
2023       lisUnableToCreateNewFileBecauseThereIsAlreadyADirecto,
2024       mtError,[mbCancel]);
2025     exit(mrCancel);
2026   end;
2027 
2028   if NewOwner is TProject then
2029     AProject:=TProject(NewOwner)
2030   else
2031     AProject:=Project1;
2032   if NewOwner is TLazPackage then
2033     APackage:=TLazPackage(NewOwner)
2034   else
2035     APackage:=nil;
2036 
2037   OldUnitIndex:=AProject.IndexOfFilename(NewFilename);
2038   if OldUnitIndex>=0 then begin
2039     // the file is not really new
2040     // => close form
2041     Result:=CloseUnitComponent(AProject.Units[OldUnitIndex],
2042                                [cfCloseDependencies,cfSaveDependencies]);
2043     if Result<>mrOk then
2044     begin
2045       debugln(['NewFile CloseUnitComponent failed']);
2046       exit;
2047     end;
2048   end;
2049 
2050   IsPartOfProject:=(nfIsPartOfProject in NewFlags)
2051                    or (NewOwner is TProject)
2052                    or (AProject.FileIsInProjectDir(NewFilename)
2053                        and (not (nfIsNotPartOfProject in NewFlags)));
2054 
2055   // add required packages
2056   //debugln(['NewFile NewFileDescriptor.RequiredPackages="',NewFileDescriptor.RequiredPackages,'" ',DbgSName(NewFileDescriptor)]);
2057   RequiredPackages:=NewFileDescriptor.RequiredPackages;
2058   if (RequiredPackages='') and (NewFileDescriptor.ResourceClass<>nil) then
2059   begin
2060     if (NewFileDescriptor.ResourceClass.InheritsFrom(TForm))
2061     or (NewFileDescriptor.ResourceClass.InheritsFrom(TFrame)) then
2062       RequiredPackages:='LCL';
2063   end;
2064   if RequiredPackages<>'' then
2065   begin
2066     if IsPartOfProject then begin
2067       Result:=PkgBoss.AddProjectDependencies(Project1,RequiredPackages);
2068       if Result<>mrOk then
2069       begin
2070         debugln(['NewFile PkgBoss.AddProjectDependencies failed RequiredPackages="',RequiredPackages,'"']);
2071         exit;
2072       end;
2073     end;
2074     if APackage<>nil then
2075     begin
2076       Result:=PkgBoss.AddPackageDependency(APackage,RequiredPackages);
2077       if Result<>mrOk then
2078       begin
2079         debugln(['NewFile PkgBoss.AddPackageDependency failed RequiredPackages="',RequiredPackages,'"']);
2080         exit;
2081       end;
2082     end;
2083   end;
2084 
2085   // check if the new file fits
2086   Result:=NewFileDescriptor.CheckOwner(nfQuiet in NewFlags);
2087   if Result<>mrOk then
2088   begin
2089     debugln(['NewFile NewFileDescriptor.CheckOwner failed NewFilename="',NewFilename,'"']);
2090     exit;
2091   end;
2092 
2093   // create new codebuffer and apply naming conventions
2094   NewBuffer:=nil;
2095   NewUnitName:='';
2096   Result:=CreateNewCodeBuffer(NewFileDescriptor,NewOwner,NewFilename,NewBuffer,NewUnitName);
2097   if Result<>mrOk then
2098   begin
2099     debugln(['NewFile CreateNewCodeBuffer failed NewFilename="',NewFilename,'"']);
2100     exit;
2101   end;
2102   NewFilename:=NewBuffer.Filename;
2103 
2104   if OldUnitIndex>=0 then begin
2105     // the file is not really new
2106     NewUnitInfo:=AProject.Units[OldUnitIndex];
2107     // assign source
2108     NewUnitInfo.Source:=NewBuffer;
2109   end else
2110     NewUnitInfo:=TUnitInfo.Create(NewBuffer);
2111   //debugln(['NewFile ',NewUnitInfo.Filename,' ',NewFilename]);
2112   if (CompareText(NewUnitInfo.Unit_Name,NewUnitName)=0) then
2113     NewUnitInfo.Unit_Name:=NewUnitName;
2114   NewUnitInfo.BuildFileIfActive:=NewFileDescriptor.BuildFileIfActive;
2115   NewUnitInfo.RunFileIfActive:=NewFileDescriptor.RunFileIfActive;
2116 
2117   // create source code
2118   //debugln('NewFile A nfCreateDefaultSrc=',nfCreateDefaultSrc in NewFlags,' ResourceClass=',dbgs(NewFileDescriptor.ResourceClass));
2119   if nfCreateDefaultSrc in NewFlags then begin
2120     if (NewFileDescriptor.ResourceClass<>nil) then begin
2121       NewUnitInfo.ComponentName:=NewUniqueComponentName(NewFileDescriptor.DefaultResourceName);
2122       NewUnitInfo.ComponentResourceName:='';
2123     end;
2124     Src:=NewFileDescriptor.CreateSource(NewUnitInfo.Filename,NewUnitName,NewUnitInfo.ComponentName);
2125     Src:=SourceEditorManager.Beautify(Src);
2126     //debugln(['NewFile ',dbgtext(Src)]);
2127     Src:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(Src,0);
2128     NewUnitInfo.Source.Source:=Src;
2129   end else begin
2130     if nfBeautifySrc in NewFlags then
2131       NewBuffer.Source:=BeautifySrc(NewSource)
2132     else
2133       NewBuffer.Source:=NewSource;
2134   end;
2135   NewUnitInfo.Modified:=true;
2136 
2137   // add to project
2138   NewUnitInfo.Loaded:=true;
2139   NewUnitInfo.IsPartOfProject:=IsPartOfProject;
2140   if OldUnitIndex<0 then begin
2141     AProject.AddFile(NewUnitInfo,
2142                      NewFileDescriptor.AddToProject
2143                      and NewFileDescriptor.IsPascalUnit
2144                      and NewUnitInfo.IsPartOfProject
2145                      and (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags));
2146   end;
2147 
2148   // syntax highlighter type
2149   NewUnitInfo.DefaultSyntaxHighlighter := FilenameToLazSyntaxHighlighter(NewFilename);
2150 
2151   NewSrcEdit := Nil;
2152   if nfOpenInEditor in NewFlags then begin
2153     // open a new sourceeditor
2154     SrcNoteBook := SourceEditorManager.ActiveOrNewSourceWindow;
2155     AShareEditor := nil;
2156     if NewUnitInfo.OpenEditorInfoCount > 0 then
2157       AShareEditor := TSourceEditor(NewUnitInfo.OpenEditorInfo[0].EditorComponent);
2158     NewSrcEdit := SrcNoteBook.NewFile(
2159       CreateSrcEditPageName(NewUnitInfo.Unit_Name, NewUnitInfo.Filename, AShareEditor),
2160       NewUnitInfo.Source, True, AShareEditor);
2161     MainIDEBar.itmFileClose.Enabled:=True;
2162     MainIDEBar.itmFileCloseAll.Enabled:=True;
2163     NewSrcEdit.SyntaxHighlighterType:=NewUnitInfo.EditorInfo[0].SyntaxHighlighter;
2164     NewUnitInfo.GetClosedOrNewEditorInfo.EditorComponent := NewSrcEdit;
2165     NewSrcEdit.EditorComponent.CaretXY := Point(1,1);
2166 
2167     // create component
2168     AncestorType:=NewFileDescriptor.ResourceClass;
2169     if AncestorType <> nil then
2170     begin
2171       // loop for Inherited Items
2172       for i:=0 to BaseFormEditor1.StandardDesignerBaseClassesCount - 1 do
2173         if AncestorType.InheritsFrom(BaseFormEditor1.StandardDesignerBaseClasses[i]) then
2174         begin
2175           LFindDesignerBaseClassByName := False;
2176           Break;
2177         end;
2178       if LFindDesignerBaseClassByName then
2179         AncestorType:=FormEditor1.FindDesignerBaseClassByName(AncestorType.ClassName, True);
2180     end;
2181     //DebugLn(['NewFile AncestorType=',dbgsName(AncestorType),' ComponentName',NewUnitInfo.ComponentName]);
2182     if AncestorType<>nil then begin
2183       ResType:=MainBuildBoss.GetResourceType(NewUnitInfo);
2184       LFMSourceText:=NewFileDescriptor.GetResourceSource(NewUnitInfo.ComponentName);
2185       //DebugLn(['NewFile LFMSourceText=',LFMSourceText]);
2186       if LFMSourceText<>'' then begin
2187         // the NewFileDescriptor provides a custom .lfm source
2188         // -> put it into a new .lfm buffer and load it
2189         LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm');
2190         LFMCode:=CodeToolBoss.CreateFile(LFMFilename);
2191         LFMCode.Source:=LFMSourceText;
2192         //debugln('NewFile A ',LFMFilename);
2193         Result:=LoadLFM(NewUnitInfo,LFMCode,[],[]);
2194         //DebugLn(['NewFile ',dbgsName(NewUnitInfo.Component),' ',dbgsName(NewUnitInfo.Component.ClassParent)]);
2195         // make sure the .lrs file exists
2196         if (ResType=rtLRS) and NewUnitInfo.IsVirtual then begin
2197           LRSFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lrs');
2198           CodeToolBoss.CreateFile(LRSFilename);
2199         end;
2200         if (NewUnitInfo.Component is TCustomForm)
2201         and NewFileDescriptor.UseCreateFormStatements
2202         and NewUnitInfo.IsPartOfProject
2203         and AProject.AutoCreateForms
2204         and (pfMainUnitHasCreateFormStatements in AProject.Flags) then
2205         begin
2206           AProject.AddCreateFormToProjectFile(NewUnitInfo.Component.ClassName,
2207                                               NewUnitInfo.Component.Name);
2208         end;
2209       end else begin
2210         // create a designer form for a form/datamodule/frame
2211         //DebugLn(['NewFile Name=',NewFileDescriptor.Name,' Class=',NewFileDescriptor.ClassName]);
2212         DisableAutoSize:=true;
2213         Result := CreateNewForm(NewUnitInfo, AncestorType, nil,
2214                                 NewFileDescriptor.UseCreateFormStatements,
2215                                 DisableAutoSize);
2216         if DisableAutoSize and (NewUnitInfo.Component<>nil)
2217         and (NewUnitInfo.Component is TControl) then
2218         begin
2219           // disable autosizing for docked form editor forms, see issue #32207
2220           PreventAutoSize := (IDETabMaster <> nil)
2221                              and (NewUnitInfo.Component is TCustomDesignControl)
2222                              and IDETabMaster.AutoSizeInShowDesigner(TControl(NewUnitInfo.Component));
2223           if not PreventAutoSize then
2224             TControl(NewUnitInfo.Component).EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster Delayed'){$ENDIF};
2225         end;
2226       end;
2227       if Result<>mrOk then
2228       begin
2229         debugln(['NewFile create designer form failed ',NewUnitInfo.Filename]);
2230         exit;
2231       end;
2232     end;
2233 
2234     // show form and select form
2235     if NewUnitInfo.Component<>nil then begin
2236       // show form
2237       MainIDE.DoShowDesignerFormOfCurrentSrc(False);
2238     end else begin
2239       MainIDE.DisplayState:= dsSource;
2240     end;
2241   end else begin
2242     // do not open in editor
2243   end;
2244 
2245   // Update HasResources property (if the .lfm file was created separately)
2246   if (not NewUnitInfo.HasResources)
2247   and FilenameIsPascalUnit(NewUnitInfo.Filename) then begin
2248     //debugln('NewFile no HasResources ',NewUnitInfo.Filename);
2249     LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm');
2250     SearchFlags:=[];
2251     if NewUnitInfo.IsPartOfProject then
2252       Include(SearchFlags,pfsfOnlyProjectFiles);
2253     if NewUnitInfo.IsVirtual then
2254       Include(SearchFlags,pfsfOnlyVirtualFiles);
2255     if (AProject.UnitInfoWithFilename(LFMFilename,SearchFlags)<>nil) then begin
2256       //debugln('NewFile no HasResources ',NewUnitInfo.Filename,' ResourceFile exists');
2257       NewUnitInfo.HasResources:=true;
2258     end;
2259   end;
2260 
2261   if (nfAskForFilename in NewFlags) then begin
2262     // save and ask for filename
2263     NewUnitInfo.Modified:=true;
2264     Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles,sfSaveAs]);
2265     if Result<>mrOk then
2266     begin
2267       debugln(['NewFile SaveEditorFile failed ',NewFilename]);
2268       exit;
2269     end;
2270   end else if nfSave in NewFlags then begin
2271     if (nfOpenInEditor in NewFlags) or NewBuffer.IsVirtual then begin
2272       // save and ask for filename if needed
2273       NewUnitInfo.Modified:=true;
2274       Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles]);
2275       if Result<>mrOk then
2276       begin
2277         debugln(['NewFile SaveEditorFile SaveAs failed ',NewFilename]);
2278         exit;
2279       end;
2280     end else begin
2281       // save quietly
2282       NewBuffer.Save;
2283     end;
2284   end;
2285 
2286   Result:=mrOk;
2287   //DebugLn('NewFile END ',NewUnitInfo.Filename);
2288   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('NewUnit end');{$ENDIF}
2289 end;
2290 
NewOthernull2291 function NewOther: TModalResult;
2292 var
2293   NewIDEItem: TNewIDEItemTemplate;
2294   NewProjFile: TNewItemProjectFile;
2295 begin
2296   Result:=ShowNewIDEItemDialog(NewIDEItem);
2297   if Result<>mrOk then exit;
2298   if NewIDEItem is TNewItemProjectFile then begin
2299     // file
2300     NewProjFile:=TNewItemProjectFile(NewIDEItem);
2301     if NewProjFile.Descriptor<>nil then
2302       NewProjFile.Descriptor.Owner:=Project1;
2303     try
2304       Result:=MainIDE.DoNewEditorFile(NewProjFile.Descriptor,
2305                                       '','',[nfOpenInEditor,nfCreateDefaultSrc]);
2306     finally
2307       if NewProjFile.Descriptor<>nil then
2308         NewProjFile.Descriptor.Owner:=nil;
2309     end;
2310   end else if NewIDEItem is TNewItemProject then       // project
2311     Result:=MainIDE.DoNewProject(TNewItemProject(NewIDEItem).Descriptor)
2312   else if NewIDEItem is TNewItemPackage then           // packages
2313     PkgBoss.DoNewPackage
2314   else
2315     IDEMessageDialog(ueNotImplCap, lisSorryThisTypeIsNotYetImplemented, mtInformation,[mbOk]);
2316 end;
2317 
NewUnitOrFormnull2318 function NewUnitOrForm(Template: TNewIDEItemTemplate;
2319   DefaultDesc: TProjectFileDescriptor): TModalResult;
2320 var
2321   Desc: TProjectFileDescriptor;
2322   Flags: TNewFlags;
2323 begin
2324   if (Template is TNewItemProjectFile) and Template.VisibleInNewDialog then
2325     Desc:=TNewItemProjectFile(Template).Descriptor
2326   else
2327     Desc:=DefaultDesc;
2328   Flags:=[nfOpenInEditor,nfCreateDefaultSrc];
2329   if (not Project1.IsVirtual) and EnvironmentOptions.AskForFilenameOnNewFile then
2330     Flags:=Flags+[nfAskForFilename,nfSave];
2331   Desc.Owner:=Project1;
2332   try
2333     Result := MainIDE.DoNewEditorFile(Desc,'','',Flags);
2334   finally
2335     Desc.Owner:=nil;
2336   end;
2337 end;
2338 
2339 procedure CreateFileDialogFilterForSourceEditorFiles(Filter: string;
2340   out AllEditorMask, AllMask: string);
2341 // Filter: a TFileDialog filter, e.g. Pascal|*.pas;*.pp|Text|*.txt
2342 // AllEditorExt: a mask for all open files in the source editor, that are not
2343 //               in Filter, e.g. '*.txt;*.xml'
2344 // AllFilter: all masks of Filter and AllEditorExt, e.g. '*.pas;*.pp;*.inc'
2345 var
2346   i: Integer;
2347   SrcEdit: TSourceEditor;
2348   Ext: String;
2349 begin
2350   AllMask:='|'+TFileDialog.ExtractAllFilterMasks(Filter);
2351   AllEditorMask:='|';
2352   for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
2353     SrcEdit:=SourceEditorManager.SourceEditors[i];
2354     Ext:=ExtractFileExt(SrcEdit.FileName);
2355     if Ext<>'' then begin
2356       Ext:='*'+Ext;
2357       if (TFileDialog.FindMaskInFilter(AllMask,Ext)>0)
2358       or (TFileDialog.FindMaskInFilter(AllEditorMask,Ext)>0) then continue;
2359       if AllEditorMask<>'|' then
2360         AllEditorMask:=AllEditorMask+';';
2361       AllEditorMask:=AllEditorMask+Ext;
2362     end;
2363   end;
2364   System.Delete(AllMask,1,1);
2365   System.Delete(AllEditorMask,1,1);
2366   if AllEditorMask<>'' then begin
2367     if AllMask<>'' then
2368       AllMask:=AllMask+';';
2369     AllMask:=AllMask+AllEditorMask;
2370   end;
2371 end;
2372 
SaveEditorFilenull2373 function SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult;
2374 var
2375   AnUnitInfo, MainUnitInfo: TUnitInfo;
2376   TestFilename, DestFilename: string;
2377   LRSCode, LFMCode: TCodeBuffer;
2378   OldUnitName, OldFilename: String;
2379   NewUnitName, NewFilename: String;
2380   WasVirtual, WasPascalSource, CanAbort, Confirm: Boolean;
2381   SaveProjectFlags: TSaveFlags;
2382   EMacro: TEditorMacro;
2383 begin
2384   Result:=mrCancel;
2385   CanAbort:=[sfCanAbort,sfProjectSaving]*Flags<>[];
2386   //debugln('SaveEditorFile A PageIndex=',PageIndex,' Flags=',SaveFlagsToString(Flags));
2387   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('SaveEditorFile A');{$ENDIF}
2388   if not (MainIDE.ToolStatus in [itNone,itDebugger]) then
2389     exit(mrAbort);
2390   if AEditor=nil then exit(mrCancel);
2391   AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
2392   if AnUnitInfo=nil then exit(mrCancel);
2393 
2394   // do not save a unit which is currently reverting
2395   if AnUnitInfo.IsReverting then
2396     exit(mrOk);
2397 
2398   WasVirtual:=AnUnitInfo.IsVirtual;
2399   WasPascalSource:=FilenameIsPascalSource(AnUnitInfo.Filename);
2400 
2401   // if this file is part of a virtual project then save the project first
2402   if (not (sfProjectSaving in Flags)) and Project1.IsVirtual and AnUnitInfo.IsPartOfProject then
2403   begin
2404     SaveProjectFlags:=Flags*[sfSaveToTestDir];
2405     if AnUnitInfo=Project1.MainUnitInfo then
2406       Include(SaveProjectFlags,sfSaveMainSourceAs);
2407     Result:=SaveProject(SaveProjectFlags);
2408     exit;
2409   end;
2410 
2411   // update codetools cache and collect Modified flags
2412   if not (sfProjectSaving in Flags) then
2413     SaveEditorChangesToCodeCache(nil);
2414 
2415   if (uifInternalFile in AnUnitInfo.Flags) then
2416   begin
2417     if (copy(AnUnitInfo.Filename, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive)
2418     then begin
2419       // save to macros
2420       EMacro := MacroListViewer.MacroByFullName(AnUnitInfo.Filename);
2421       if EMacro <> nil then begin
2422         EMacro.SetFromSource(AEditor.SourceText);
2423         if EMacro.IsInvalid and (EMacro.ErrorMsg <> '') then
2424           IDEMessagesWindow.AddCustomMessage(mluError,EMacro.ErrorMsg);
2425       end;
2426       MacroListViewer.UpdateDisplay;
2427       AnUnitInfo.ClearModifieds;
2428       AEditor.Modified:=false;
2429     end;
2430     // otherwise unknown internal file => skip
2431     exit(mrOk);
2432   end;
2433 
2434   // if this is a new unit then a simple Save becomes a SaveAs
2435   if (not (sfSaveToTestDir in Flags)) and (AnUnitInfo.IsVirtual) then
2436     Include(Flags,sfSaveAs);
2437 
2438   // if this is the main source and has the same name as the lpi
2439   // rename the project
2440   // Note:
2441   //   Changing the main source file without the .lpi is possible only by
2442   //   manually editing the lpi file, because this is only needed in
2443   //   special cases (rare functions don't need front ends).
2444   MainUnitInfo:=AnUnitInfo.Project.MainUnitInfo;
2445   if (sfSaveAs in Flags) and (not (sfProjectSaving in Flags)) and (AnUnitInfo=MainUnitInfo)
2446   then begin
2447     Result:=SaveProject([sfSaveAs,sfSaveMainSourceAs]);
2448     exit;
2449   end;
2450 
2451   // if nothing modified then a simple Save can be skipped
2452   //debugln(['SaveEditorFile A ',AnUnitInfo.Filename,' ',AnUnitInfo.NeedsSaveToDisk]);
2453   if ([sfSaveToTestDir,sfSaveAs]*Flags=[]) and (not AnUnitInfo.NeedsSaveToDisk) then
2454   begin
2455     if AEditor.Modified then
2456     begin
2457       AnUnitInfo.SessionModified:=true;
2458       AEditor.Modified:=false;
2459     end;
2460     exit(mrOk);
2461   end;
2462 
2463   // check if file is writable on disk
2464   if (not AnUnitInfo.IsVirtual) and FileExistsUTF8(AnUnitInfo.Filename) then
2465     AnUnitInfo.FileReadOnly:=not FileIsWritable(AnUnitInfo.Filename)
2466   else
2467     AnUnitInfo.FileReadOnly:=false;
2468 
2469   // if file is readonly then a simple Save is skipped
2470   if (AnUnitInfo.ReadOnly) and ([sfSaveToTestDir,sfSaveAs]*Flags=[]) then
2471     exit(mrOk);
2472 
2473   // load old resource file
2474   LFMCode:=nil;
2475   LRSCode:=nil;
2476   if WasPascalSource then
2477   begin
2478     Result:=LoadResourceFile(AnUnitInfo,LFMCode,LRSCode,true,CanAbort);
2479     if not (Result in [mrIgnore,mrOk]) then
2480       exit;
2481   end;
2482 
2483   OldUnitName:='';
2484   if WasPascalSource then
2485     OldUnitName:=AnUnitInfo.ReadUnitNameFromSource(true);
2486   OldFilename:=AnUnitInfo.Filename;
2487 
2488   if [sfSaveAs,sfSaveToTestDir]*Flags=[sfSaveAs] then begin
2489     // let user choose a filename
2490     NewFilename:=OldFilename;
2491     Result:=ShowSaveFileAsDialog(NewFilename,AnUnitInfo,LFMCode,LRSCode,CanAbort);
2492     if not (Result in [mrIgnore,mrOk]) then
2493       exit;
2494   end;
2495 
2496   // save source
2497 
2498   // a) do before save events
2499   if EditorOpts.AutoRemoveEmptyMethods and (AnUnitInfo.Component<>nil) then begin
2500     // Note: When removing published methods, the source, the lfm, the lrs
2501     //       and the form must be changed. At the moment editing the lfm without
2502     //       the component is not yet implemented.
2503     Result:=RemoveEmptyMethods(AnUnitInfo.Source,
2504                    AnUnitInfo.Component.ClassName,0,0,false,[pcsPublished]);
2505     if Result=mrAbort then exit;
2506   end;
2507 
2508   // b) do actual save
2509   DestFilename := '';
2510   if (sfSaveToTestDir in Flags) or AnUnitInfo.IsVirtual then
2511   begin
2512     // save source to test directory
2513     TestFilename := MainBuildBoss.GetTestUnitFilename(AnUnitInfo);
2514     if TestFilename <> '' then
2515     begin
2516       DestFilename := TestFilename;
2517       // notify packages
2518       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,
2519                                                   sefsBeforeWrite,DestFilename);
2520       if Result<>mrOk then exit;
2521       // actual write
2522       //DebugLn(['SaveEditorFile TestFilename="',TestFilename,'" Size=',AnUnitInfo.Source.SourceLength]);
2523       Result := AnUnitInfo.WriteUnitSourceToFile(DestFilename);
2524       if Result <> mrOk then
2525         Exit;
2526       // notify packages
2527       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,
2528                                                    sefsAfterWrite,DestFilename);
2529       if Result<>mrOk then exit;
2530     end
2531     else
2532       exit(mrCancel);
2533   end else
2534   begin
2535     if AnUnitInfo.Modified or (MainIDE.CheckFilesOnDiskEnabled and AnUnitInfo.NeedsSaveToDisk) then
2536     begin
2537       // save source to file
2538       DestFilename := AnUnitInfo.Filename;
2539       // notify packages
2540       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,
2541         AnUnitInfo,sefsBeforeWrite,DestFilename);
2542       if Result<>mrOk then exit;
2543       // actual write
2544       Result := AnUnitInfo.WriteUnitSource;
2545       if Result <> mrOK then
2546         exit;
2547       // notify packages
2548       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,
2549         AnUnitInfo,sefsAfterWrite,DestFilename);
2550       if Result<>mrOk then exit;
2551     end;
2552   end;
2553 
2554   if sfCheckAmbiguousFiles in Flags then
2555     MainBuildBoss.CheckAmbiguousSources(DestFilename,false);
2556 
2557   {$IFDEF IDE_DEBUG}
2558   debugln(['*** HasResources=',AnUnitInfo.HasResources]);
2559   {$ENDIF}
2560   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('SaveEditorFile B');{$ENDIF}
2561   // save resource file and lfm file
2562   if (LRSCode<>nil) or (AnUnitInfo.Component<>nil) then begin
2563     Result:=SaveUnitComponent(AnUnitInfo,LRSCode,LFMCode,Flags);
2564     if not (Result in [mrIgnore, mrOk]) then
2565       exit;
2566   end;
2567 
2568   // unset all modified flags
2569   if not (sfSaveToTestDir in Flags) then begin
2570     AnUnitInfo.ClearModifieds;
2571     AEditor.Modified:=false;
2572     MainIDE.UpdateSaveMenuItemsAndButtons(not (sfProjectSaving in Flags));
2573   end;
2574   TSourceEditor(AEditor).SourceNotebook.UpdateStatusBar;
2575 
2576   // fix all references
2577   NewUnitName:='';
2578   if FilenameIsPascalSource(AnUnitInfo.Filename) then
2579     NewUnitName:=AnUnitInfo.ReadUnitNameFromSource(true);
2580   NewFilename:=AnUnitInfo.Filename;
2581   if (NewUnitName<>'')
2582   and ((OldUnitName<>NewUnitName) or (CompareFilenames(OldFilename,NewFilename)<>0))
2583   then begin
2584     if EnvironmentOptions.UnitRenameReferencesAction<>urraNever then
2585     begin
2586       // silently update references of new units (references were auto created
2587       // and keeping old references makes no sense)
2588       Confirm:=(EnvironmentOptions.UnitRenameReferencesAction=urraAsk)
2589                and (not WasVirtual);
2590       Result:=ReplaceUnitUse(OldFilename,OldUnitName,NewFilename,NewUnitName,
2591                true,true,Confirm);
2592       if Result<>mrOk then exit;
2593     end;
2594   end;
2595 
2596   {$IFDEF IDE_VERBOSE}
2597   debugln(['SaveEditorFile END ',NewFilename,' AnUnitInfo.Modified=',AnUnitInfo.Modified,' AEditor.Modified=',AEditor.Modified]);
2598   {$ENDIF}
2599   Result:=mrOk;
2600 end;
2601 
2602 function SaveEditorFile(const Filename: string; Flags: TSaveFlags): TModalResult;
2603 var
2604   UnitIndex: Integer;
2605   AnUnitInfo: TUnitInfo;
2606   i: Integer;
2607 begin
2608   Result:=mrOk;
2609   if Filename='' then exit;
2610   UnitIndex:=Project1.IndexOfFilename(TrimFilename(Filename),[pfsfOnlyEditorFiles]);
2611   if UnitIndex<0 then exit;
2612   AnUnitInfo:=Project1.Units[UnitIndex];
2613   for i := 0 to AnUnitInfo.OpenEditorInfoCount-1 do begin
2614     Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[i].EditorComponent, Flags);
2615     if Result <> mrOK then Break;
2616   end;
2617 end;
2618 
2619 function CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags): TModalResult;
2620 var
2621   AnUnitInfo: TUnitInfo;
2622   ACaption, AText: string;
2623   i: integer;
2624   AnEditorInfo: TUnitEditorInfo;
2625   SrcEditWasFocused: Boolean;
2626   SrcEdit: TSourceEditor;
2627 begin
2628   {$IFDEF IDE_DEBUG}
2629   //debugln('CloseEditorFile A PageIndex=',IntToStr(AnUnitInfo.PageIndex));
2630   {$ENDIF}
2631   Result:=mrCancel;
2632   if AEditor = nil then exit;
2633   AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor);
2634   //AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
2635   if AnEditorInfo = nil then begin
2636     // we need to close the page anyway or else we might enter a loop
2637     DebugLn('CloseEditorFile INCONSISTENCY: NO AnUnitInfo');
2638     SourceEditorManager.CloseFile(AEditor);
2639     Result:=mrOk;
2640     exit;
2641   end;
2642   AnUnitInfo := AnEditorInfo.UnitInfo;
2643   AnUnitInfo.SessionModified:=true;
2644   SrcEditWasFocused:=(AnEditorInfo.EditorComponent<>nil)
2645      and (AnEditorInfo.EditorComponent.EditorControl<>nil)
2646      and AnEditorInfo.EditorComponent.EditorControl.Focused;
2647   //debugln(['CloseEditorFile File=',AnUnitInfo.Filename,' WasFocused=',SrcEditWasFocused]);
2648   try
2649     //debugln(['CloseEditorFile File=',AnUnitInfo.Filename,' UnitSession=',AnUnitInfo.SessionModified,' ProjSession=',project1.SessionModified]);
2650     if AnUnitInfo.OpenEditorInfoCount > 1 then begin
2651       // opened multiple times => close one instance
2652       SourceEditorManager.CloseFile(AEditor);
2653       Result:=mrOk;
2654       exit;
2655     end;
2656 
2657     if (AnUnitInfo.Component<>nil) and (MainIDE.LastFormActivated<>nil)
2658     and (MainIDE.LastFormActivated.Designer.LookupRoot=AnUnitInfo.Component) then
2659       MainIDE.LastFormActivated:=nil;
2660 
2661     // save some meta data of the source
2662     SaveSrcEditorProjectSpecificSettings(AnEditorInfo);
2663 
2664     // if SaveFirst then save the source
2665     if (cfSaveFirst in Flags) and (not AnUnitInfo.ReadOnly)
2666     and ((AEditor.Modified) or (AnUnitInfo.Modified)) then begin
2667       if not (cfQuiet in Flags) then begin
2668         // ask user
2669         if AnUnitInfo.Filename<>'' then
2670           AText:=Format(lisFileHasChangedSave, [AnUnitInfo.Filename])
2671         else if AnUnitInfo.Unit_Name<>'' then
2672           AText:=Format(lisUnitHasChangedSave, [AnUnitInfo.Unit_Name])
2673         else
2674           AText:=Format(lisSourceOfPageHasChangedSave, [TSourceEditor(AEditor).PageName]);
2675         ACaption:=lisSourceModified;
2676         Result:=IDEQuestionDialog(ACaption, AText,
2677             mtConfirmation, [mrYes, lisMenuSave,
2678                              mrNo, lisDiscardChanges,
2679                              mrAbort]);
2680       end else
2681         Result:=mrYes;
2682       if Result=mrYes then begin
2683         Result:=SaveEditorFile(AnEditorInfo.EditorComponent,[sfCheckAmbiguousFiles]);
2684       end;
2685       if Result in [mrAbort,mrCancel] then exit;
2686       Result:=mrOk;
2687     end;
2688     // mark file as unmodified
2689     if (AnUnitInfo.Source<>nil) and AnUnitInfo.Source.Modified then
2690       AnUnitInfo.Source.Clear;
2691 
2692     // add to recent file list
2693     if (not AnUnitInfo.IsVirtual) and (not (cfProjectClosing in Flags)) then
2694     begin
2695       EnvironmentOptions.AddToRecentOpenFiles(AnUnitInfo.Filename);
2696       MainIDE.SetRecentFilesMenu;
2697     end;
2698 
2699     // close form soft (keep it if used by another component)
2700     CloseUnitComponent(AnUnitInfo,[]);
2701 
2702     // close source editor
2703     SourceEditorManager.CloseFile(AnEditorInfo.EditorComponent);
2704     MainIDEBar.itmFileClose.Enabled:=SourceEditorManager.SourceEditorCount > 0;
2705     MainIDEBar.itmFileCloseAll.Enabled:=MainIDEBar.itmFileClose.Enabled;
2706 
2707     // free sources, forget changes
2708     if (AnUnitInfo.Source<>nil) then begin
2709       if (Project1.MainUnitInfo=AnUnitInfo)
2710       and (not (cfProjectClosing in Flags)) then begin
2711         AnUnitInfo.Source.Revert;
2712       end else begin
2713         AnUnitInfo.Source.IsDeleted:=true;
2714       end;
2715     end;
2716 
2717     // close file in project
2718     AnUnitInfo.Loaded:=false;
2719     if AnUnitInfo<>Project1.MainUnitInfo then
2720       AnUnitInfo.Source:=nil;
2721     if not (cfProjectClosing in Flags) then begin
2722       i:=Project1.IndexOf(AnUnitInfo);
2723       if (i<>Project1.MainUnitID) and AnUnitInfo.IsVirtual then begin
2724         Project1.RemoveUnit(i);
2725       end;
2726     end;
2727 
2728   finally
2729     if SrcEditWasFocused then begin
2730       // before closing the syendit was focused. Focus the current synedit.
2731       SrcEdit := SourceEditorManager.ActiveEditor;
2732       if (SrcEdit<>nil)
2733       and (SrcEdit.EditorControl<>nil)
2734       and (SrcEdit.EditorControl.CanFocus) then
2735         SrcEdit.EditorControl.SetFocus;
2736       //debugln(['CloseEditorFile Focus=',SrcEdit.FileName,' Editor=',DbgSName(SrcEdit.EditorControl),' Focused=',(SrcEdit.EditorControl<>nil) and (SrcEdit.EditorControl.Focused)]);
2737     end;
2738   end;
2739   {$IFDEF IDE_DEBUG}
2740   DebugLn('CloseEditorFile end');
2741   {$ENDIF}
2742   Result:=mrOk;
2743 end;
2744 
2745 function CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult;
2746 var
2747   UnitIndex: Integer;
2748   AnUnitInfo: TUnitInfo;
2749 begin
2750   Result:=mrOk;
2751   if Filename='' then exit;
2752   UnitIndex:=Project1.IndexOfFilename(TrimFilename(Filename),[pfsfOnlyEditorFiles]);
2753   if UnitIndex<0 then exit;
2754   AnUnitInfo:=Project1.Units[UnitIndex];
2755   while (AnUnitInfo.OpenEditorInfoCount > 0) and (Result = mrOK) do
2756     Result:=CloseEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, Flags);
2757 end;
2758 
2759 function FindUnitFileImpl(const AFilename: string; TheOwner: TObject;
2760   Flags: TFindUnitFileFlags): string;
2761 
2762   function FindInBaseIDE: string;
2763   var
2764     AnUnitName: String;
2765     BaseDir: String;
2766     UnitInFilename: String;
2767   begin
2768     AnUnitName:=ExtractFileNameOnly(AFilename);
2769     BaseDir:=EnvironmentOptions.GetParsedLazarusDirectory+PathDelim+'ide';
2770     UnitInFilename:='';
2771     Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2772                                        BaseDir,AnUnitName,UnitInFilename,true);
2773   end;
2774 
2775   function FindInProject(AProject: TProject): string;
2776   var
2777     AnUnitInfo: TUnitInfo;
2778     AnUnitName: String;
2779     BaseDir: String;
2780     UnitInFilename: String;
2781   begin
2782     // search in virtual (unsaved) files
2783     AnUnitInfo:=AProject.UnitInfoWithFilename(AFilename,
2784                                      [pfsfOnlyProjectFiles,pfsfOnlyVirtualFiles]);
2785     if AnUnitInfo<>nil then begin
2786       Result:=AnUnitInfo.Filename;
2787       exit;
2788     end;
2789 
2790     // search in search path of project
2791     AnUnitName:=ExtractFileNameOnly(AFilename);
2792     BaseDir:=AProject.Directory;
2793     UnitInFilename:='';
2794     Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2795                                        BaseDir,AnUnitName,UnitInFilename,true);
2796   end;
2797 
2798   function FindInPackage(APackage: TLazPackage): string;
2799   var
2800     BaseDir: String;
2801     AnUnitName: String;
2802     UnitInFilename: String;
2803   begin
2804     Result:='';
2805     BaseDir:=APackage.Directory;
2806     if not FilenameIsAbsolute(BaseDir) then exit;
2807     // search in search path of package
2808     AnUnitName:=ExtractFileNameOnly(AFilename);
2809     UnitInFilename:='';
2810     Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2811                                        BaseDir,AnUnitName,UnitInFilename,true);
2812   end;
2813 
2814 var
2815   AProject: TProject;
2816   i: Integer;
2817 begin
2818   if FilenameIsAbsolute(AFilename) then begin
2819     Result:=AFilename;
2820     exit;
2821   end;
2822   Result:='';
2823 
2824   // project
2825   AProject:=nil;
2826   if TheOwner=nil then begin
2827     AProject:=Project1;
2828   end else if (TheOwner is TProject) then
2829     AProject:=TProject(TheOwner);
2830 
2831   if AProject<>nil then
2832   begin
2833     Result:=FindInProject(AProject);
2834     if Result<>'' then exit;
2835   end;
2836 
2837   // package
2838   if TheOwner is TLazPackage then begin
2839     Result:=FindInPackage(TLazPackage(TheOwner));
2840     if Result<>'' then exit;
2841   end;
2842 
2843   if TheOwner=LazarusIDE then begin
2844     // search in base IDE
2845     Result:=FindInBaseIDE;
2846     if Result<>'' then exit;
2847 
2848     // search in installed packages
2849     for i:=0 to PackageGraph.Count-1 do
2850       if (PackageGraph[i].Installed<>pitNope)
2851       and ((not (fuffIgnoreUninstallPackages in Flags))
2852            or (PackageGraph[i].AutoInstall<>pitNope))
2853       then begin
2854         Result:=FindInPackage(PackageGraph[i]);
2855         if Result<>'' then exit;
2856       end;
2857     // search in auto install packages
2858     for i:=0 to PackageGraph.Count-1 do
2859       if (PackageGraph[i].Installed=pitNope)
2860       and (PackageGraph[i].AutoInstall<>pitNope) then begin
2861         Result:=FindInPackage(PackageGraph[i]);
2862         if Result<>'' then exit;
2863       end;
2864     // then search in all other open packages
2865     for i:=0 to PackageGraph.Count-1 do
2866       if (PackageGraph[i].Installed=pitNope)
2867       and (PackageGraph[i].AutoInstall=pitNope) then begin
2868         Result:=FindInPackage(PackageGraph[i]);
2869         if Result<>'' then exit;
2870       end;
2871   end;
2872   Result:='';
2873 end;
2874 
2875 function FindSourceFileImpl(const AFilename, BaseDirectory: string;
2876   Flags: TFindSourceFlags): string;
2877 // AFilename can be an absolute or relative filename, of a source file or a
2878 // compiled unit (.ppu).
2879 // Find the source filename (pascal source or include file) and returns
2880 // the absolute path.
2881 // With fsfMapTempToVirtualFiles files in the temp directory are stripped off
2882 // the temporary files resulting in the virtual file name of the CodeTools.
2883 //
2884 // First it searches in the current projects src path, then its unit path, then
2885 // its include path. Then all used package source directories are searched.
2886 // Finally the fpc sources are searched.
2887 var
2888   CompiledSrcExt: String;
2889   BaseDir: String;
2890   AlreadySearchedPaths: string;
2891   StartUnitPath: String;
2892 
2893   procedure MarkPathAsSearched(const AddSearchPath: string);
2894   begin
2895     AlreadySearchedPaths:=MergeSearchPaths(AlreadySearchedPaths,AddSearchPath);
2896   end;
2897 
2898   function SearchIndirectIncludeFile: string;
2899   var
2900     UnitPath: String;
2901     CurDir: String;
2902     AlreadySearchedUnitDirs: String;
2903     CompiledUnitPath: String;
2904     AllSrcPaths: String;
2905     CurSrcPath: String;
2906     CurIncPath: String;
2907     PathPos: Integer;
2908     AllIncPaths: String;
2909     SearchPath: String;
2910     SearchFile: String;
2911   begin
2912     if CompiledSrcExt='' then exit('');
2913     // get unit path for compiled units
2914     UnitPath:=BaseDir+';'+StartUnitPath;
2915     UnitPath:=TrimSearchPath(UnitPath,BaseDir);
2916 
2917     // Extract all directories with compiled units
2918     CompiledUnitPath:='';
2919     AlreadySearchedUnitDirs:='';
2920     PathPos:=1;
2921     while PathPos<=length(UnitPath) do begin
2922       CurDir:=GetNextDirectoryInSearchPath(UnitPath,PathPos);
2923       // check if directory is already tested
2924       if SearchDirectoryInSearchPath(AlreadySearchedUnitDirs,CurDir,1)>0 then
2925         continue;
2926       AlreadySearchedUnitDirs:=MergeSearchPaths(AlreadySearchedUnitDirs,CurDir);
2927       // check if directory contains a compiled unit
2928       if FindFirstFileWithExt(CurDir,CompiledSrcExt)<>'' then
2929         CompiledUnitPath:=CompiledUnitPath+';'+CurDir;
2930     end;
2931     {$IFDEF VerboseFindSourceFile}
2932     debugln(['SearchIndirectIncludeFile CompiledUnitPath="',CompiledUnitPath,'"']);
2933     {$ENDIF}
2934 
2935     // collect all src paths for the compiled units
2936     AllSrcPaths:=CompiledUnitPath;
2937     PathPos:=1;
2938     while PathPos<=length(CompiledUnitPath) do begin
2939       CurDir:=GetNextDirectoryInSearchPath(CompiledUnitPath,PathPos);
2940       CurSrcPath:=CodeToolBoss.GetCompiledSrcPathForDirectory(CurDir);
2941       CurSrcPath:=TrimSearchPath(CurSrcPath,CurDir);
2942       AllSrcPaths:=MergeSearchPaths(AllSrcPaths,CurSrcPath);
2943     end;
2944     {$IFDEF VerboseFindSourceFile}
2945     debugln(['SearchIndirectIncludeFile AllSrcPaths="',AllSrcPaths,'"']);
2946     {$ENDIF}
2947 
2948     // add fpc src directories
2949     // ToDo
2950 
2951     // collect all include paths
2952     AllIncPaths:=AllSrcPaths;
2953     PathPos:=1;
2954     while PathPos<=length(AllSrcPaths) do begin
2955       CurDir:=GetNextDirectoryInSearchPath(AllSrcPaths,PathPos);
2956       CurIncPath:=CodeToolBoss.GetIncludePathForDirectory(CurDir);
2957       CurIncPath:=TrimSearchPath(CurIncPath,CurDir);
2958       AllIncPaths:=MergeSearchPaths(AllIncPaths,CurIncPath);
2959     end;
2960     {$IFDEF VerboseFindSourceFile}
2961     debugln(['SearchIndirectIncludeFile AllIncPaths="',AllIncPaths,'"']);
2962     {$ENDIF}
2963 
2964     SearchFile:=AFilename;
2965     SearchPath:=AllIncPaths;
2966     Result:=FileUtil.SearchFileInPath(SearchFile,BaseDir,SearchPath,';',[]);
2967     {$IFDEF VerboseFindSourceFile}
2968     debugln(['SearchIndirectIncludeFile Result="',Result,'"']);
2969     {$ENDIF}
2970     MarkPathAsSearched(SearchPath);
2971   end;
2972 
2973   function SearchInPath(const TheSearchPath, SearchFile: string;
2974     var Filename: string): boolean;
2975   var
2976     SearchPath: String;
2977   begin
2978     Filename:='';
2979     SearchPath:=RemoveSearchPaths(TheSearchPath,AlreadySearchedPaths);
2980     if SearchPath<>'' then begin
2981       Filename:=FileUtil.SearchFileInPath(SearchFile,BaseDir,SearchPath,';',[]);
2982       {$IFDEF VerboseFindSourceFile}
2983       debugln(['FindSourceFile trying "',SearchPath,'" Filename="',Filename,'"']);
2984       {$ENDIF}
2985       MarkPathAsSearched(SearchPath);
2986     end;
2987     Result:=Filename<>'';
2988   end;
2989 
2990 var
2991   SearchPath: String;
2992   SearchFile: String;
2993   ProjFile: TLazProjectFile;
2994 begin
2995   {$IFDEF VerboseFindSourceFile}
2996   debugln(['FindSourceFile Filename="',AFilename,'" BaseDirectory="',BaseDirectory,'"']);
2997   {$ENDIF}
2998   if AFilename='' then exit('');
2999 
3000   if fsfMapTempToVirtualFiles in Flags then
3001   begin
3002     BaseDir:=MainBuildBoss.GetTestBuildDirectory;
3003     if FilenameIsAbsolute(AFilename)
3004     and FileIsInPath(AFilename,BaseDir) then
3005     begin
3006       Result:=CreateRelativePath(AFilename,BaseDir);
3007       if (Project1<>nil) and (Project1.UnitInfoWithFilename(Result)<>nil) then
3008         exit;
3009     end;
3010   end;
3011 
3012   if FilenameIsAbsolute(AFilename) then
3013   begin
3014     Result := AFilename;
3015     if not FileExistsCached(Result) then
3016       Result := '';
3017     Exit;
3018   end;
3019 
3020   AlreadySearchedPaths:='';
3021   BaseDir:=BaseDirectory;
3022   GlobalMacroList.SubstituteStr(BaseDir);
3023   BaseDir:=AppendPathDelim(TrimFilename(BaseDir));
3024 
3025   // search file in base directory
3026   if FilenameIsAbsolute(BaseDir) then begin
3027     Result:=TrimFilename(BaseDir+AFilename);
3028     {$IFDEF VerboseFindSourceFile}
3029     debugln(['FindSourceFile trying Base "',Result,'"']);
3030     {$ENDIF}
3031     if FileExistsCached(Result) then exit;
3032     MarkPathAsSearched(BaseDir);
3033   end else if Project1<>nil then begin
3034     // search in virtual files
3035     Result:=TrimFilename(BaseDir+AFilename);
3036     ProjFile:=Project1.FindFile(Result,[pfsfOnlyVirtualFiles]);
3037     if ProjFile<>nil then
3038       exit;
3039   end;
3040 
3041   // search file in debug path
3042   if (fsfUseDebugPath in Flags) and (Project1<>nil) then begin
3043     SearchPath:=EnvironmentOptions.GetParsedDebuggerSearchPath;
3044     SearchPath:=MergeSearchPaths(Project1.CompilerOptions.GetDebugPath(false),
3045                                  SearchPath);
3046     SearchPath:=TrimSearchPath(SearchPath,BaseDir);
3047     if SearchInPath(SearchPath,AFilename,Result) then exit;
3048   end;
3049 
3050   CompiledSrcExt:=CodeToolBoss.GetCompiledSrcExtForDirectory(BaseDir);
3051   StartUnitPath:=CodeToolBoss.GetCompleteSrcPathForDirectory(BaseDir);
3052   StartUnitPath:=TrimSearchPath(StartUnitPath,BaseDir);
3053 
3054   // if file is a pascal unit, search via unit and src paths
3055   if FilenameIsPascalUnit(AFilename) then begin
3056     // first search file in unit path
3057     if SearchInPath(StartUnitPath,AFilename,Result) then exit;
3058 
3059     // search unit in fpc source directory
3060     Result:=CodeToolBoss.FindUnitInUnitSet(BaseDir,
3061                                            ExtractFilenameOnly(AFilename));
3062     {$IFDEF VerboseFindSourceFile}
3063     debugln(['FindSourceFile tried unitset Result=',Result]);
3064     {$ENDIF}
3065     if Result<>'' then exit;
3066   end;
3067 
3068   if fsfUseIncludePaths in Flags then begin
3069     // search in include path
3070     if (fsfSearchForProject in Flags) then
3071       SearchPath:=Project1.CompilerOptions.GetIncludePath(false)
3072     else
3073       SearchPath:=CodeToolBoss.GetIncludePathForDirectory(BaseDir);
3074     SearchPath:=TrimSearchPath(SearchPath,BaseDir);
3075     if SearchInPath(StartUnitPath,AFilename,Result) then exit;
3076 
3077     if not(fsfSkipPackages in Flags) then begin
3078       // search include file in source directories of all required packages
3079       SearchFile:=AFilename;
3080       Result:=PkgBoss.FindIncludeFileInProjectDependencies(Project1,SearchFile);
3081       {$IFDEF VerboseFindSourceFile}
3082       debugln(['FindSourceFile trying packages "',SearchPath,'" Result=',Result]);
3083       {$ENDIF}
3084     end;
3085     if Result<>'' then exit;
3086 
3087     Result:=SearchIndirectIncludeFile;
3088     if Result<>'' then exit;
3089   end;
3090 
3091   Result:='';
3092 end;
3093 
3094 function FindUnitsOfOwnerImpl(TheOwner: TObject; Flags: TFindUnitsOfOwnerFlags): TStrings;
3095 var
3096   Files: TFilenameToStringTree;
3097   UnitPath: string; // only if not AddPackages:
3098                     // owner unitpath without unitpaths of required packages
3099 
3100   function Add(const aFilename: string): boolean;
3101   begin
3102     if Files.Contains(aFilename) then exit(false);
3103     //debugln(['  Add ',aFilename]);
3104     Files[aFilename]:='';
3105     FindUnitsOfOwnerImpl.Add(aFilename);
3106     Result := True;
3107   end;
3108 
3109   procedure AddListedPackageUnits(aPackage: TLazPackage);
3110   // add listed units of aPackage
3111   var
3112     i: Integer;
3113     PkgFile: TPkgFile;
3114   begin
3115     //debugln([' AddListedPackageUnits ',aPackage.IDAsString]);
3116     for i:=0 to aPackage.FileCount-1 do
3117     begin
3118       PkgFile:=aPackage.Files[i];
3119       if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
3120       if not PkgFile.InUses then continue;
3121       Add(PkgFile.Filename);
3122     end;
3123   end;
3124 
3125   procedure AddUsedUnit(const aFilename: string);
3126   // add recursively all units
3127 
3128     procedure AddUses(UsesSection: TStrings);
3129     var
3130       i: Integer;
3131       Code: TCodeBuffer;
3132     begin
3133       if UsesSection=nil then exit;
3134       for i:=0 to UsesSection.Count-1 do begin
3135         //debugln(['AddUses ',UsesSection[i]]);
3136         Code:=TCodeBuffer(UsesSection.Objects[i]);
3137         if Code=nil then exit;
3138         AddUsedUnit(Code.Filename);
3139       end;
3140     end;
3141 
3142   var
3143     Code: TCodeBuffer;
3144     MainUsesSection, ImplementationUsesSection: TStrings;
3145   begin
3146     //debugln(['  AddUsedUnit START ',aFilename]);
3147     if not (fuooPackages in Flags) then
3148     begin
3149       if FilenameIsAbsolute(aFilename) then
3150       begin
3151         if SearchDirectoryInSearchPath(UnitPath,ExtractFilePath(aFilename))<0 then
3152           exit; // not in exclusive unitpath
3153       end else begin
3154         if (not (TheOwner is TProject)) or (not TProject(TheOwner).IsVirtual) then
3155           exit;
3156       end;
3157     end;
3158     //debugln(['  AddUsedUnit OK ',aFilename]);
3159     if not Add(aFilename) then exit;
3160     Code:=CodeToolBoss.LoadFile(aFilename,true,false);
3161     if Code=nil then exit;
3162     MainUsesSection:=nil;
3163     ImplementationUsesSection:=nil;
3164     try
3165       CodeToolBoss.FindUsedUnitFiles(Code,MainUsesSection,ImplementationUsesSection);
3166       AddUses(MainUsesSection);
3167       AddUses(ImplementationUsesSection);
3168     finally
3169       MainUsesSection.Free;
3170       ImplementationUsesSection.Free;
3171     end;
3172   end;
3173 
3174 var
3175   aProject: TProject;
3176   aPackage, ReqPackage: TLazPackage;
3177   MainFile, CurFilename: String;
3178   AnUnitInfo: TUnitInfo;
3179   i: Integer;
3180   Code: TCodeBuffer;
3181   FoundInUnits, MissingUnits, NormalUnits: TStrings;
3182   PkgList: TFPList;
3183   PkgListFlags: TPkgIntfRequiredFlags;
3184 begin
3185   Result:=TStringList.Create;
3186   MainFile:='';
3187   FoundInUnits:=nil;
3188   MissingUnits:=nil;
3189   NormalUnits:=nil;
3190   aProject:=nil;
3191   aPackage:=nil;
3192   PkgList:=nil;
3193   Files:=TFilenameToStringTree.Create(false);
3194   try
3195     //debugln(['FindUnitsOfOwner ',DbgSName(TheOwner)]);
3196     if TheOwner is TProject then
3197     begin
3198       aProject:=TProject(TheOwner);
3199       // add main project source (e.g. .lpr)
3200       if (aProject.MainFile<>nil) and (pfMainUnitIsPascalSource in aProject.Flags)
3201       then begin
3202         MainFile:=aProject.MainFile.Filename;
3203         Add(MainFile);
3204       end;
3205       if (fuooListed in Flags) then begin
3206         // add listed units (i.e. units in project inspector)
3207         AnUnitInfo:=aProject.FirstPartOfProject;
3208         while AnUnitInfo<>nil do
3209         begin
3210           if FilenameIsPascalUnit(AnUnitInfo.Filename) then
3211             Add(AnUnitInfo.Filename);
3212           AnUnitInfo:=AnUnitInfo.NextPartOfProject;
3213         end;
3214       end;
3215       if (fuooListed in Flags) and (fuooPackages in Flags) then
3216       begin
3217         // get required packages
3218         if pfUseDesignTimePackages in aProject.Flags then
3219           PkgListFlags:=[]
3220         else
3221           PkgListFlags:=[pirSkipDesignTimeOnly];
3222         PackageGraph.GetAllRequiredPackages(nil,aProject.FirstRequiredDependency,
3223           PkgList,PkgListFlags);
3224       end;
3225     end else if TheOwner is TLazPackage then begin
3226       aPackage:=TLazPackage(TheOwner);
3227       if (fuooListed in Flags) then
3228       begin
3229         // add listed units (i.e. units in package editor)
3230         AddListedPackageUnits(aPackage);
3231       end;
3232       if (fuooUsed in Flags) then
3233         MainFile:=aPackage.GetSrcFilename;
3234       if (fuooListed in Flags) and (fuooPackages in Flags) then
3235       begin
3236         // get required packages
3237         PackageGraph.GetAllRequiredPackages(aPackage,nil,PkgList,[]);
3238       end;
3239     end else begin
3240       FreeAndNil(Result);
3241       raise Exception.Create('FindUnitsOfOwner: invalid owner '+DbgSName(TheOwner));
3242     end;
3243 
3244     if (fuooListed in Flags) and (fuooPackages in Flags) and (PkgList<>nil) then begin
3245       // add package units (listed in their package editors)
3246       for i:=0 to PkgList.Count-1 do begin
3247         ReqPackage:=TLazPackage(PkgList[i]);
3248         AddListedPackageUnits(ReqPackage);
3249       end;
3250     end;
3251 
3252     if (fuooUsed in Flags) and (MainFile<>'') then
3253     begin
3254       // add all used units with 'in' files
3255       Code:=CodeToolBoss.LoadFile(MainFile,true,false);
3256       if Code<>nil then begin
3257         UnitPath:='';
3258         if aProject<>nil then begin
3259           CodeToolBoss.FindDelphiProjectUnits(Code,FoundInUnits,MissingUnits,NormalUnits);
3260           if not (fuooPackages in Flags) then
3261           begin
3262             // only project units wanted -> create unitpath excluding unitpaths from packages
3263             // Note: even if the project contains an unitpath to the source
3264             //       folder of a package, the units are not project units.
3265             UnitPath:=aProject.CompilerOptions.GetUnitPath(false);
3266             RemoveSearchPaths(UnitPath,aProject.CompilerOptions.GetInheritedOption(icoUnitPath,false));
3267           end;
3268         end
3269         else if aPackage<>nil then begin
3270           CodeToolBoss.FindDelphiPackageUnits(Code,FoundInUnits,MissingUnits,NormalUnits);
3271           if not (fuooPackages in Flags) then
3272           begin
3273             // only units of this package wanted
3274             // -> create unitpath excluding unitpaths from used packages
3275             // Note: even if the package contains an unitpath to the source
3276             //       folder of a sub package, the units belong to the sub package
3277             UnitPath:=aPackage.CompilerOptions.GetUnitPath(false);
3278             RemoveSearchPaths(UnitPath,aPackage.CompilerOptions.GetInheritedOption(icoUnitPath,false));
3279           end;
3280         end;
3281         //debugln(['FindUnitsOfOwner UnitPath="',UnitPath,'"']);
3282         if FoundInUnits<>nil then
3283           for i:=0 to FoundInUnits.Count-1 do
3284           begin
3285             CurFilename:=TCodeBuffer(FoundInUnits.Objects[i]).Filename;
3286             Add(CurFilename); // units with 'in' filename always belong to the
3287                 // project, that's the Delphi way
3288             AddUsedUnit(CurFilename);
3289           end;
3290         if NormalUnits<>nil then
3291           for i:=0 to NormalUnits.Count-1 do
3292             AddUsedUnit(TCodeBuffer(NormalUnits.Objects[i]).Filename);
3293       end;
3294     end;
3295     if (fuooSourceEditor in Flags) then
3296       for i := 0 to pred(SourceEditorManager.SourceEditorCount) do
3297       begin
3298         CurFilename := SourceEditorManager.SourceEditors[i].FileName;
3299         if FilenameIsPascalUnit(CurFilename) then
3300           Add(CurFilename);
3301       end;
3302   finally
3303     FoundInUnits.Free;
3304     MissingUnits.Free;
3305     NormalUnits.Free;
3306     PkgList.Free;
3307     Files.Free;
3308   end;
3309 end;
3310 
SelectProjectItemsnull3311 function SelectProjectItems(ItemList: TViewUnitEntries;
3312   ItemType: TIDEProjectItem; MultiSelect: boolean;
3313   var MultiSelectCheckedState: Boolean): TModalResult;
3314 var
3315   i: integer;
3316   AUnitName, DlgCaption: string;
3317   MainUnitInfo: TUnitInfo;
3318   ActiveSourceEditor: TSourceEditor;
3319   ActiveUnitInfo: TUnitInfo;
3320   CurUnitInfo: TUnitInfo;
3321   LFMFilename: String;
3322   LFMType: String;
3323   LFMComponentName: String;
3324   LFMClassName: String;
3325   anUnitName: String;
3326   LFMCode: TCodeBuffer;
3327 begin
3328   if Project1=nil then exit(mrCancel);
3329   MainIDE.GetCurrentUnit(ActiveSourceEditor, ActiveUnitInfo);
3330   for i := 0 to Project1.UnitCount - 1 do
3331   begin
3332     CurUnitInfo:=Project1.Units[i];
3333     if not CurUnitInfo.IsPartOfProject then
3334       Continue;
3335     if ItemType in [piComponent, piFrame] then
3336     begin
3337       // add all form names of project
3338       if CurUnitInfo.ComponentName <> '' then
3339       begin
3340         if (ItemType = piComponent) or
3341            ((ItemType = piFrame) and (CurUnitInfo.ResourceBaseClass = pfcbcFrame)) then
3342           ItemList.Add(CurUnitInfo.ComponentName,
3343                        CurUnitInfo.Filename, i, CurUnitInfo = ActiveUnitInfo);
3344       end else if FilenameIsAbsolute(CurUnitInfo.Filename)
3345       and FilenameIsPascalSource(CurUnitInfo.Filename)
3346       and FileExistsCached(CurUnitInfo.Filename) then
3347       begin
3348         // this unit has a lfm, but the lpi does not know a ComponentName
3349         // => maybe this component was added without the IDE
3350         LFMFilename:=ChangeFileExt(CurUnitInfo.Filename,'.lfm');
3351         LFMCode:=CodeToolBoss.LoadFile(LFMFilename,true,false);
3352         if LFMCode<>nil then
3353         begin
3354           ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
3355           if LFMComponentName<>'' then begin
3356             anUnitName:=CurUnitInfo.Unit_Name;
3357             if anUnitName='' then
3358               anUnitName:=ExtractFileNameOnly(LFMFilename);
3359             ItemList.Add(LFMComponentName, CurUnitInfo.Filename,
3360               i, CurUnitInfo = ActiveUnitInfo);
3361           end;
3362         end;
3363       end;
3364     end else
3365     begin
3366       // add all unit names of project
3367       if (CurUnitInfo.FileName <> '') then
3368       begin
3369         AUnitName := ExtractFileName(CurUnitInfo.Filename);
3370         if ItemList.Find(AUnitName) = nil then
3371           ItemList.Add(AUnitName, CurUnitInfo.Filename,
3372                        i, CurUnitInfo = ActiveUnitInfo);
3373       end
3374       else
3375       if Project1.MainUnitID = i then
3376       begin
3377         MainUnitInfo := Project1.MainUnitInfo;
3378         if pfMainUnitIsPascalSource in Project1.Flags then
3379         begin
3380           AUnitName := ExtractFileName(MainUnitInfo.Filename);
3381           if (AUnitName <> '') and (ItemList.Find(AUnitName) = nil) then
3382           begin
3383             ItemList.Add(AUnitName, MainUnitInfo.Filename,
3384                          i, MainUnitInfo = ActiveUnitInfo);
3385           end;
3386         end;
3387       end;
3388     end;
3389   end;
3390   case ItemType of
3391     piUnit:      DlgCaption := dlgMainViewUnits;
3392     piComponent: DlgCaption := dlgMainViewForms;
3393     piFrame:     DlgCaption := dlgMainViewFrames;
3394     else         DlgCaption := '';
3395   end;
3396   Result := ShowViewUnitsDlg(ItemList, MultiSelect, MultiSelectCheckedState, DlgCaption, ItemType);
3397 end;
3398 
SelectUnitComponentsnull3399 function SelectUnitComponents(DlgCaption: string;
3400   ItemType: TIDEProjectItem; Files: TStringList; MultiSelect: boolean;
3401   var MultiSelectCheckedState: Boolean): TModalResult;
3402 var
3403   ActiveSourceEditor: TSourceEditor;
3404   ActiveUnitInfo: TUnitInfo;
3405   UnitToFilename: TStringToStringTree;
3406   UnitPath: String;
3407 
ResourceFitsnull3408   function ResourceFits(ResourceBaseClass: TPFComponentBaseClass): boolean;
3409   begin
3410     case ItemType of
3411     piUnit: Result:=true;
3412     piComponent: Result:=ResourceBaseClass<>pfcbcNone;
3413     piFrame: Result:=ResourceBaseClass=pfcbcFrame;
3414     else Result:=false;
3415     end;
3416   end;
3417 
3418   procedure AddUnit(AnUnitName,AFilename: string);
3419   var
3420     LFMFilename: String;
3421   begin
3422     //debugln(['AddUnit ',AFilename]);
3423     if not FilenameIsPascalUnit(AFilename) then exit;
3424     if CompareFilenames(AFilename,ActiveUnitInfo.Filename)=0 then exit;
3425     if (AnUnitName='') then
3426       AnUnitName:=ExtractFileNameOnly(AFilename);
3427     if (not FilenameIsAbsolute(AFilename)) then begin
3428       if (not ActiveUnitInfo.IsVirtual) then
3429         exit; // virtual UnitToFilename can not be accessed from disk UnitToFilename
3430     end else begin
3431       //debugln(['AddUnit unitpath=',UnitPath]);
3432       if SearchDirectoryInSearchPath(UnitPath,ExtractFilePath(AFilename))<1 then
3433         exit; // not reachable
3434     end;
3435     if UnitToFilename.Contains(AnUnitName) then exit; // duplicate unit
3436     if not FileExistsCached(AFilename) then exit;
3437     LFMFilename:=ChangeFileExt(aFilename,'.lfm');
3438     if not FileExistsCached(LFMFilename) then exit;
3439     UnitToFilename[AnUnitName]:=AFilename;
3440   end;
3441 
3442   procedure AddPackage(Pkg: TLazPackage);
3443   var
3444     i: Integer;
3445     PkgFile: TPkgFile;
3446   begin
3447     //debugln(['AddPackage ',pkg.Name]);
3448     for i:=0 to Pkg.FileCount-1 do begin
3449       PkgFile:=TPkgFile(Pkg.Files[i]);
3450       if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
3451       if not FilenameIsAbsolute(PkgFile.Filename) then continue;
3452       if not ResourceFits(PkgFile.ResourceBaseClass) then begin
3453         if PkgFile.ResourceBaseClass<>pfcbcNone then continue;
3454         // unknown resource class => check file
3455         PkgFile.ResourceBaseClass:=FindLFMBaseClass(PkgFile.Filename);
3456         if not ResourceFits(PkgFile.ResourceBaseClass) then continue;
3457       end;
3458       AddUnit(PkgFile.Unit_Name,PkgFile.Filename);
3459     end;
3460   end;
3461 
3462 var
3463   Owners: TFPList;
3464   APackage: TLazPackage;
3465   AProject: TProject;
3466   AnUnitInfo: TUnitInfo;
3467   FirstDependency: TPkgDependency;
3468   PkgList: TFPList;
3469   i: Integer;
3470   S2SItem: PStringToStringItem;
3471   AnUnitName: String;
3472   AFilename: String;
3473   UnitList: TViewUnitEntries;
3474   Entry: TViewUnitsEntry;
3475 begin
3476   Result:=mrCancel;
3477   MainIDE.GetCurrentUnit(ActiveSourceEditor, ActiveUnitInfo);
3478   if ActiveUnitInfo=nil then exit;
3479   Owners:=PkgBoss.GetPossibleOwnersOfUnit(ActiveUnitInfo.Filename,[]);
3480   UnitPath:=CodeToolBoss.GetCompleteSrcPathForDirectory(ExtractFilePath(ActiveUnitInfo.Filename));
3481   PkgList:=nil;
3482   UnitToFilename:=TStringToStringTree.Create(false);
3483   UnitList:=TViewUnitEntries.Create;
3484   try
3485     // fetch owner of active unit
3486     AProject:=nil;
3487     APackage:=nil;
3488     if (Owners<>nil) then begin
3489       for i:=0 to Owners.Count-1 do begin
3490         if TObject(Owners[i]) is TProject then begin
3491           AProject:=TProject(Owners[i]);
3492           break;
3493         end else if TObject(Owners[i]) is TLazPackage then begin
3494           APackage:=TLazPackage(Owners[i]);
3495         end;
3496       end;
3497     end;
3498     if AProject<>nil then begin
3499       // add project units
3500       //debugln(['SelectUnitComponents Project=',AProject.ProjectInfoFile]);
3501       FirstDependency:=AProject.FirstRequiredDependency;
3502       for i:=0 to AProject.UnitCount-1 do begin
3503         AnUnitInfo:=AProject.Units[i];
3504         if (not AnUnitInfo.IsPartOfProject)
3505         or (AnUnitInfo.ComponentName='')
3506         then continue;
3507         if not ResourceFits(AnUnitInfo.ResourceBaseClass) then begin
3508           if AnUnitInfo.ResourceBaseClass<>pfcbcNone then continue;
3509           // unknown resource class => check file
3510           AnUnitInfo.ResourceBaseClass:=FindLFMBaseClass(AnUnitInfo.Filename);
3511           if not ResourceFits(AnUnitInfo.ResourceBaseClass) then continue;
3512         end;
3513         AddUnit(AnUnitInfo.Unit_Name,AnUnitInfo.Filename);
3514       end;
3515     end else if APackage<>nil then begin
3516       // add package units
3517       FirstDependency:=APackage.FirstRequiredDependency;
3518       AddPackage(APackage);
3519     end else
3520       FirstDependency:=nil;
3521     // add all units of all used packages
3522     PackageGraph.GetAllRequiredPackages(nil,FirstDependency,PkgList);
3523     if PkgList<>nil then
3524       for i:=0 to PkgList.Count-1 do
3525         AddPackage(TLazPackage(PkgList[i]));
3526 
3527     // create Files
3528     i:=0;
3529     for S2SItem in UnitToFilename do begin
3530       AnUnitName:=S2SItem^.Name;
3531       AFilename:=S2SItem^.Value;
3532       UnitList.Add(AnUnitName,AFilename,i,false);
3533       inc(i);
3534     end;
3535     // show dialog
3536     Result := ShowViewUnitsDlg(UnitList, MultiSelect, MultiSelectCheckedState,
3537                                DlgCaption, ItemType, ActiveUnitInfo.Filename);
3538     // create list of selected files
3539     for Entry in UnitList do
3540       if Entry.Selected then
3541         Files.Add(Entry.Filename);
3542 
3543   finally
3544     UnitList.Free;
3545     PkgList.Free;
3546     Owners.Free;
3547     UnitToFilename.Free;
3548   end;
3549 end;
3550 
AddActiveUnitToProjectnull3551 function AddActiveUnitToProject: TModalResult;
3552 begin
3553   Result := AddUnitToProject(nil);
3554 end;
3555 
RemoveFromProjectDialognull3556 function RemoveFromProjectDialog: TModalResult;
3557 var
3558   ViewUnitEntries: TViewUnitEntries;
3559   i:integer;
3560   AName: string;
3561   AnUnitInfo: TUnitInfo;
3562   UnitInfos: TFPList;
3563   UEntry: TViewUnitsEntry;
3564 const
3565   MultiSelectCheckedState: Boolean = true;
3566 Begin
3567   if Project1=nil then exit(mrCancel);
3568   Project1.UpdateIsPartOfProjectFromMainUnit;
3569   ViewUnitEntries := TViewUnitEntries.Create;
3570   UnitInfos:=nil;
3571   try
3572     for i := 0 to Project1.UnitCount-1 do
3573     begin
3574       AnUnitInfo:=Project1.Units[i];
3575       if (AnUnitInfo.IsPartOfProject) and (i<>Project1.MainUnitID) then
3576       begin
3577         AName := Project1.RemoveProjectPathFromFilename(AnUnitInfo.FileName);
3578         ViewUnitEntries.Add(AName,AnUnitInfo.FileName,i,false);
3579       end;
3580     end;
3581     if ShowViewUnitsDlg(ViewUnitEntries, true, MultiSelectCheckedState,
3582           lisRemoveFromProject, piUnit) <> mrOk then
3583       exit(mrOk);
3584     { This is where we check what the user selected. }
3585     UnitInfos:=TFPList.Create;
3586     for UEntry in ViewUnitEntries do
3587     begin
3588       if UEntry.Selected then
3589       begin
3590         if UEntry.ID<0 then continue;
3591         AnUnitInfo:=Project1.Units[UEntry.ID];
3592         if AnUnitInfo.IsPartOfProject then
3593           UnitInfos.Add(AnUnitInfo);
3594       end;
3595     end;
3596     if UnitInfos.Count>0 then
3597       Result:=RemoveFilesFromProject(UnitInfos)
3598     else
3599       Result:=mrOk;
3600   finally
3601     UnitInfos.Free;
3602     ViewUnitEntries.Free;
3603   end;
3604 end;
3605 
InitNewProjectnull3606 function InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult;
3607 var
3608   i:integer;
3609   HandlerResult: TModalResult;
3610 begin
3611   try
3612     Project1.BeginUpdate(true);
3613     try
3614       if Project1.CompilerOptions.CompilerPath='' then
3615         Project1.CompilerOptions.CompilerPath:=DefaultCompilerPath;
3616       if pfUseDefaultCompilerOptions in Project1.Flags then begin
3617         MainIDE.DoMergeDefaultProjectOptions(Project1);
3618         Project1.Flags:=Project1.Flags-[pfUseDefaultCompilerOptions];
3619       end;
3620       Project1.AutoAddOutputDirToIncPath;
3621       MainIDE.UpdateCaption;
3622       if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3623       // add and load default required packages
3624       PkgBoss.OpenProjectDependencies(Project1,true);
3625       // rebuild codetools defines
3626       MainBuildBoss.SetBuildTargetProject1(false);
3627       // (i.e. remove old project specific things and create new)
3628       IncreaseCompilerParseStamp;
3629       Project1.DefineTemplates.AllChanged;
3630       Project1.DefineTemplates.Active:=true;
3631       DebugBoss.Reset;
3632     finally
3633       Project1.EndUpdate;
3634     end;
3635     Project1.BeginUpdate(true);
3636     try
3637       // create files
3638       if ProjectDesc.CreateStartFiles(Project1)<>mrOk then begin
3639         debugln('InitNewProject ProjectDesc.CreateStartFiles failed');
3640       end;
3641       if (Project1.MainUnitInfo<>nil)
3642       and ((Project1.FirstUnitWithEditorIndex=nil)
3643        or ([pfMainUnitHasCreateFormStatements,pfMainUnitHasTitleStatement,pfMainUnitHasScaledStatement]*Project1.Flags=[]))
3644       then begin
3645         // the project has not created any secondary files
3646         // or the project main source is not auto updated by the IDE
3647         OpenMainUnit(-1,-1,[]);
3648       end;
3649 
3650       // init resource files
3651       if not Project1.ProjResources.Regenerate(Project1.MainFilename, True, False,'') then
3652         DebugLn('InitNewProject Project1.Resources.Regenerate failed');
3653     finally
3654       Project1.EndUpdate;
3655     end;
3656     Result:=mrOk;
3657   finally
3658     // set all modified to false
3659     Project1.UpdateAllVisibleUnits;
3660     for i:=0 to Project1.UnitCount-1 do
3661       Project1.Units[i].ClearModifieds;
3662     Project1.Modified:=false;
3663     // call handlers
3664     HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1);
3665     if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then
3666       HandlerResult:=mrCancel;
3667     if (Result=mrOk) then
3668       Result:=HandlerResult;
3669   end;
3670 end;
3671 
InitOpenedProjectFilenull3672 function InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult;
3673 var
3674   EditorInfoIndex, i, j: Integer;
3675   NewBuf: TCodeBuffer;
3676   LastDesigner: TIDesigner;
3677   AnUnitInfo: TUnitInfo;
3678   HandlerResult: TModalResult;
3679   AnEditorInfo: TUnitEditorInfo;
3680 begin
3681   EditorInfoIndex := 0;
3682   SourceEditorManager.IncUpdateLock;
3683   try
3684     Project1.BeginUpdate(true);
3685     try
3686       if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3687 
3688       // read project info file
3689       {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B3');{$ENDIF}
3690       Project1.ReadProject(AFilename, EnvironmentOptions.BuildMatrixOptions, True);
3691       {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B4');{$ENDIF}
3692       Result:=CompleteLoadingProjectInfo;
3693     finally
3694       Project1.EndUpdate;
3695     end;
3696     {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B5');{$ENDIF}
3697     if Result<>mrOk then exit;
3698 
3699     if Project1.MainUnitID>=0 then begin
3700       // read MainUnit Source
3701       Result:=LoadCodeBuffer(NewBuf,Project1.MainFilename,
3702                              [lbfUpdateFromDisk,lbfRevert],false);// do not check if source is text
3703       case Result of
3704       mrOk: Project1.MainUnitInfo.Source:=NewBuf;
3705       mrIgnore: Project1.MainUnitInfo.Source:=CodeToolBoss.CreateFile(Project1.MainFilename);
3706       else exit(mrCancel);
3707       end;
3708     end;
3709     //debugln('InitOpenedProjectFile C');
3710     {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile C');{$ENDIF}
3711     IncreaseCompilerParseStamp;
3712 
3713     // restore files
3714     while EditorInfoIndex < Project1.AllEditorsInfoCount do begin
3715       // TProject.ReadProject sorts all UnitEditorInfos
3716       AnEditorInfo := Project1.AllEditorsInfo[EditorInfoIndex];
3717       AnUnitInfo := AnEditorInfo.UnitInfo;
3718       if (not AnUnitInfo.Loaded) or (AnEditorInfo.PageIndex < 0) then begin
3719         inc(EditorInfoIndex);
3720         Continue;
3721       end;
3722 
3723       // reopen file
3724       if (not AnUnitInfo.IsPartOfProject)
3725       and (not FileExistsCached(AnUnitInfo.Filename)) then begin
3726         // this file does not exist, but is not important => silently ignore
3727       end
3728       else begin
3729         // reopen file
3730         // This will adjust Page/WindowIndex if they are not continous
3731         Result:=OpenEditorFile(AnUnitInfo.Filename, -1, AnEditorInfo.WindowID,
3732                       AnEditorInfo, [ofProjectLoading,ofMultiOpen,ofOnlyIfExists], True);
3733         if Result=mrAbort then
3734           exit;
3735       end;
3736       if not ((AnUnitInfo.Filename<>'') and (AnEditorInfo.EditorComponent <> nil))
3737       then begin
3738         // failed to open
3739         AnEditorInfo.PageIndex := -1;
3740         // if failed entirely -> mark as unloaded, so that next time it will not be tried again
3741         if AnUnitInfo.OpenEditorInfoCount = 0 then
3742           AnUnitInfo.Loaded := False;
3743       end;
3744       inc(EditorInfoIndex);
3745     end; // while EditorInfoIndex < Project1.AllEditorsInfoCount
3746     Result:=mrCancel;
3747     //debugln('InitOpenedProjectFile D');
3748 
3749     // set active editor source editor
3750     for i := 0 to Project1.AllEditorsInfoCount - 1 do begin
3751       AnEditorInfo := Project1.AllEditorsInfo[i];
3752       if AnEditorInfo.IsVisibleTab then
3753       begin
3754         if (AnEditorInfo.WindowID < 0) then continue;
3755         j := SourceEditorManager.IndexOfSourceWindowWithID(AnEditorInfo.WindowID);
3756         if j < 0
3757         then begin
3758           // session info is invalid (buggy lps file?) => auto fix
3759           AnEditorInfo.IsVisibleTab:=false;
3760           AnEditorInfo.WindowID:=-1;
3761           Continue;
3762         end;
3763         if (SourceEditorManager.SourceWindows[j] <> nil) then
3764           SourceEditorManager.SourceWindows[j].PageIndex := AnEditorInfo.PageIndex;
3765       end;
3766     end;
3767     if (Project1.ActiveWindowIndexAtStart<0)
3768     or (Project1.ActiveWindowIndexAtStart >= SourceEditorManager.SourceWindowCount)
3769     then begin
3770       // session info is invalid (buggy lps file?) => auto fix
3771       Project1.ActiveWindowIndexAtStart := 0;
3772     end;
3773     if (Project1.ActiveWindowIndexAtStart >= 0) and
3774        (Project1.ActiveWindowIndexAtStart < SourceEditorManager.SourceWindowCount)
3775     then begin
3776       SourceEditorManager.ActiveSourceWindow :=
3777         SourceEditorManager.SourceWindows[Project1.ActiveWindowIndexAtStart];
3778       SourceEditorManager.ShowActiveWindowOnTop(True);
3779     end;
3780 
3781     if ([ofDoNotLoadResource]*Flags=[])
3782     and ( (not Project1.AutoOpenDesignerFormsDisabled)
3783            and EnvironmentOptions.AutoCreateFormsOnOpen
3784            and (SourceEditorManager.ActiveEditor<>nil) )
3785     then begin
3786       // auto open form of active unit
3787       AnUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor);
3788       if AnUnitInfo<>nil then
3789         Result:=LoadLFM(AnUnitInfo,[ofProjectLoading,ofMultiOpen,ofOnlyIfExists],
3790                           [cfSaveDependencies]);
3791     end;
3792 
3793     // select a form (object inspector, formeditor, control selection)
3794     if MainIDE.LastFormActivated<>nil then begin
3795       LastDesigner:=MainIDE.LastFormActivated.Designer;
3796       debugln(['InitOpenedProjectFile select form in designer: ',
3797                DbgSName(MainIDE.LastFormActivated),' ',DbgSName(MainIDE.LastFormActivated.Designer)]);
3798       LastDesigner.SelectOnlyThisComponent(LastDesigner.LookupRoot);
3799     end;
3800 
3801     // set all modified to false
3802     Project1.UpdateAllVisibleUnits;
3803     Project1.ClearModifieds(true);
3804 
3805     IncreaseCompilerParseStamp;
3806     IDEProtocolOpts.LastProjectLoadingCrashed := False;
3807     Result:=mrOk;
3808   finally
3809     SourceEditorManager.DecUpdateLock;
3810     if (Result<>mrOk) and (Project1<>nil) then begin
3811       // mark all files, that are left to open as unloaded:
3812       for i := EditorInfoIndex to Project1.AllEditorsInfoCount - 1 do begin
3813         AnEditorInfo := Project1.AllEditorsInfo[i];
3814         AnEditorInfo.PageIndex := -1;
3815         AnUnitInfo := AnEditorInfo.UnitInfo;
3816         if AnUnitInfo.Loaded and (AnUnitInfo.OpenEditorInfoCount = 0) then
3817           AnUnitInfo.Loaded := false;
3818       end;
3819     end;
3820     // call handlers
3821     HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1);
3822     if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then
3823       HandlerResult:=mrCancel;
3824     if (Result=mrOk) then
3825       Result:=HandlerResult;
3826   end;
3827   if Result=mrAbort then exit;
3828   //debugln('InitOpenedProjectFile end  CodeToolBoss.ConsistencyCheck=',IntToStr(CodeToolBoss.ConsistencyCheck));
3829   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile end');{$ENDIF}
3830 end;
3831 
3832 procedure NewProjectFromFile;
3833 var
3834   OpenDialog:TOpenDialog;
3835   AFilename: string;
3836   PreReadBuf: TCodeBuffer;
3837   Filter: String;
3838 Begin
3839   OpenDialog:=TOpenDialog.Create(nil);
3840   try
3841     InputHistories.ApplyFileDialogSettings(OpenDialog);
3842     OpenDialog.Title:=lisChooseProgramSourcePpPasLpr;
3843     OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist,ofFileMustExist];
3844     Filter := dlgFilterLazarusUnit + ' (*.pas;*.pp;*.p)|*.pas;*.pp;*.p'
3845       + '|' + dlgFilterLazarusProjectSource + ' (*.lpr)|*.lpr';
3846     Filter:=Filter+ '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
3847     OpenDialog.Filter := Filter;
3848     if OpenDialog.Execute then begin
3849       AFilename:=ExpandFileNameUTF8(OpenDialog.Filename);
3850       if not FilenameIsPascalSource(AFilename) then begin
3851         IDEMessageDialog(lisPkgMangInvalidFileExtension,
3852           lisProgramSourceMustHaveAPascalExtensionLikePasPpOrLp,
3853           mtError,[mbOk],'');
3854         exit;
3855       end;
3856       if mrOk<>LoadCodeBuffer(PreReadBuf,AFileName,
3857                               [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert],false)
3858       then
3859         exit;
3860       if CreateProjectForProgram(PreReadBuf)=mrOk then
3861         exit;
3862     end;
3863   finally
3864     InputHistories.StoreFileDialogSettings(OpenDialog);
3865     OpenDialog.Free;
3866   end;
3867 end;
3868 
CreateProjectForProgramnull3869 function CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
3870 var
3871   NewProjectDesc: TProjectDescriptor;
3872 begin
3873   //debugln('[CreateProjectForProgram] A ',ProgramBuf.Filename);
3874   if (Project1 <> nil)
3875   and (not MainIDE.DoResetToolStatus([rfInteractive, rfSuccessOnTrigger])) then exit(mrAbort);
3876 
3877   Result:=SaveProjectIfChanged;
3878   if Result=mrAbort then exit;
3879 
3880   // let user choose the program type
3881   NewProjectDesc:=nil;
3882   if ChooseNewProject(NewProjectDesc)<>mrOk then exit;
3883 
3884   // close old project
3885   If Project1<>nil then begin
3886     if CloseProject=mrAbort then begin
3887       Result:=mrAbort;
3888       exit;
3889     end;
3890   end;
3891 
3892   // reload file (if the file was open in the IDE, closeproject unloaded it)
3893   ProgramBuf.Reload;
3894 
3895   // switch codetools to new project directory
3896   CodeToolBoss.GlobalValues.Variables[ExternalMacroStart+'ProjPath']:=
3897     ExpandFileNameUTF8(ExtractFilePath(ProgramBuf.Filename));
3898 
3899   // create a new project
3900   Project1:=MainIDE.CreateProjectObject(NewProjectDesc,ProjectDescriptorProgram);
3901   Result:=InitProjectForProgram(ProgramBuf);
3902   //debugln('[CreateProjectForProgram] END');
3903 end;
3904 
InitProjectForProgramnull3905 function InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
3906 var
3907   MainUnitInfo: TUnitInfo;
3908 begin
3909   Project1.BeginUpdate(true);
3910   try
3911     if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3912     MainUnitInfo:=Project1.MainUnitInfo;
3913     MainUnitInfo.Source:=ProgramBuf;
3914     Project1.ProjectInfoFile:=ChangeFileExt(ProgramBuf.Filename,'.lpi');
3915     Project1.CompilerOptions.TargetFilename:=ExtractFileNameOnly(ProgramBuf.Filename);
3916     MainIDE.DoMergeDefaultProjectOptions(Project1);
3917     MainIDE.UpdateCaption;
3918     IncreaseCompilerParseStamp;
3919     // add and load default required packages
3920     PkgBoss.OpenProjectDependencies(Project1,true);
3921     Result:=CompleteLoadingProjectInfo;
3922     if Result<>mrOk then exit;
3923   finally
3924     Project1.EndUpdate;
3925   end;
3926   // show program unit
3927   Result:=OpenEditorFile(ProgramBuf.Filename,-1,-1, nil, [ofAddToRecent,ofRegularFile]);
3928   if Result=mrAbort then exit;
3929   Result:=mrOk;
3930 end;
3931 
SaveProjectnull3932 function SaveProject(Flags: TSaveFlags):TModalResult;
3933 var
3934   i, j: integer;
3935   AnUnitInfo: TUnitInfo;
3936   SaveFileFlags: TSaveFlags;
3937   SrcEdit: TSourceEditor;
3938 begin
3939   Result:=mrCancel;
3940   if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin
3941     Result:=mrAbort;
3942     exit;
3943   end;
3944   SaveEditorChangesToCodeCache(nil);
3945   //DebugLn('SaveProject A SaveAs=',dbgs(sfSaveAs in Flags),' SaveToTestDir=',dbgs(sfSaveToTestDir in Flags),' ProjectInfoFile=',Project1.ProjectInfoFile);
3946   Result:=MainIDE.DoCheckFilesOnDisk(true);
3947   if Result in [mrCancel,mrAbort] then begin
3948     debugln(['Info: (lazarus) [SaveProject] MainIDE.DoCheckFilesOnDisk failed']);
3949     exit;
3950   end;
3951 
3952   if CheckMainSrcLCLInterfaces(sfQuietUnitCheck in Flags)<>mrOk then begin
3953     debugln(['Info: (lazarus) [SaveProject] CheckMainSrcLCLInterfaces failed']);
3954     exit(mrCancel);
3955   end;
3956 
3957   // if this is a virtual project then save first the project info file
3958   // to get a project directory
3959   if Project1.IsVirtual and ([sfSaveToTestDir,sfDoNotSaveVirtualFiles]*Flags=[])
3960   then begin
3961     Result:=SaveProjectInfo(Flags);
3962     if Result in [mrCancel,mrAbort] then begin
3963       debugln(['Info: (lazarus) [SaveProject] SaveProjectInfo failed']);
3964       exit;
3965     end;
3966   end;
3967 
3968   // save virtual files
3969   if (not (sfDoNotSaveVirtualFiles in Flags)) then
3970   begin
3971     // check that all new units are saved first to get valid filenames
3972     // Note: this can alter the mainunit: e.g. used unit names
3973     for i:=0 to Project1.UnitCount-1 do begin
3974       AnUnitInfo:=Project1.Units[i];
3975       if (AnUnitInfo.Loaded) and AnUnitInfo.IsVirtual
3976       and AnUnitInfo.IsPartOfProject
3977       and (Project1.MainUnitID<>i)
3978       and (AnUnitInfo.OpenEditorInfoCount > 0) then begin
3979         SaveFileFlags:=[sfSaveAs,sfProjectSaving]+[sfCheckAmbiguousFiles]*Flags;
3980         if sfSaveToTestDir in Flags then begin
3981           Assert(AnUnitInfo.IsPartOfProject or AnUnitInfo.IsVirtual, 'SaveProject: Not IsPartOfProject or IsVirtual');
3982           Include(SaveFileFlags,sfSaveToTestDir);
3983         end;
3984         Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, SaveFileFlags);
3985         if Result in [mrCancel,mrAbort] then begin
3986           debugln(['Info: (lazarus) [SaveProject] SaveEditorFile "',AnUnitInfo.Filename,'" failed']);
3987           exit;
3988         end;
3989       end;
3990     end;
3991   end;
3992 
3993   Result:=SaveProjectInfo(Flags);
3994   if Result in [mrCancel,mrAbort] then begin
3995     debugln(['Info: (lazarus) [SaveProject] SaveProjectInfo failed']);
3996     exit;
3997   end;
3998 
3999   // save all editor files
4000   for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
4001     SrcEdit:=SourceEditorManager.SourceEditors[i];
4002     AnUnitInfo:=Project1.UnitWithEditorComponent(SrcEdit);
4003     if (Project1.MainUnitID>=0) and (Project1.MainUnitInfo = AnUnitInfo) then
4004       continue;
4005     SaveFileFlags:=[sfProjectSaving]+Flags*[sfCheckAmbiguousFiles];
4006     if AnUnitInfo = nil
4007     then begin
4008       // inconsistency detected, write debug info
4009       DebugLn(['SaveProject - unit not found for page ',i,' File="',SrcEdit.FileName,'" SrcEdit=',dbgsname(SrcEdit),'=',dbgs(Pointer(SrcEdit))]);
4010       DumpStack;
4011       debugln(['SaveProject Project1 has the following information about the source editor:']);
4012       AnUnitInfo:=Project1.FirstUnitWithEditorIndex;
4013       while AnUnitInfo<>nil do begin
4014         for j:=0 to AnUnitInfo.EditorInfoCount-1 do begin
4015           dbgout(['  ',AnUnitInfo.Filename,' ',j,'/',AnUnitInfo.EditorInfoCount,' Component=',dbgsname(AnUnitInfo.EditorInfo[j].EditorComponent),'=',dbgs(Pointer(AnUnitInfo.EditorInfo[j].EditorComponent))]);
4016           if AnUnitInfo.EditorInfo[j].EditorComponent<>nil then
4017             dbgout(AnUnitInfo.EditorInfo[j].EditorComponent.FileName);
4018           debugln;
4019         end;
4020         debugln(['  ',AnUnitInfo.EditorInfoCount]);
4021         AnUnitInfo:=AnUnitInfo.NextUnitWithEditorIndex;
4022       end;
4023     end else begin
4024       if AnUnitInfo.IsVirtual then begin
4025         if (sfSaveToTestDir in Flags) then
4026           Include(SaveFileFlags,sfSaveToTestDir)
4027         else
4028           continue;
4029       end;
4030     end;
4031     Result:=SaveEditorFile(SrcEdit, SaveFileFlags);
4032     if Result=mrAbort then begin
4033       debugln(['Info: (lazarus) [SaveProject] SaveEditorFile "',SrcEdit.FileName,'" failed']);
4034       exit;
4035     end;
4036     // mrCancel: continue saving other files
4037   end;
4038 
4039   // update all lrs files
4040   if sfSaveToTestDir in Flags then
4041     MainBuildBoss.UpdateProjectAutomaticFiles(EnvironmentOptions.GetParsedTestBuildDirectory)
4042   else
4043     MainBuildBoss.UpdateProjectAutomaticFiles('');
4044 
4045   // everything went well => clear all modified flags
4046   Project1.ClearModifieds(true);
4047   // update menu and buttons state
4048   MainIDE.UpdateSaveMenuItemsAndButtons(true);
4049   //DebugLn('SaveProject End');
4050   Result:=mrOk;
4051 end;
4052 
SaveProjectIfChangednull4053 function SaveProjectIfChanged: TModalResult;
4054 begin
4055   if SomethingOfProjectIsModified then begin
4056     if IDEMessageDialog(lisProjectChanged, Format(lisSaveChangesToProject,
4057       [Project1.GetTitleOrName]),
4058       mtconfirmation, [mbYes, mbNo, mbCancel])=mrYes then
4059     begin
4060       if SaveProject([])=mrAbort then begin
4061         Result:=mrAbort;
4062         exit;
4063       end;
4064     end;
4065   end;
4066   Result:=mrOk;
4067 end;
4068 
CloseProjectnull4069 function CloseProject: TModalResult;
4070 var
4071   SrcEdit: TSourceEditor;
4072 begin
4073   if Project1=nil then exit(mrOk);
4074 
4075   //debugln('CloseProject A');
4076   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject A');{$ENDIF}
4077   Result:=DebugBoss.DoStopProject;
4078   if Result<>mrOk then begin
4079     debugln('CloseProject DebugBoss.DoStopProject failed');
4080     exit;
4081   end;
4082 
4083   // call handlers
4084   Result:=MainIDE.DoCallProjectChangedHandler(lihtProjectClose, Project1);
4085   if Result=mrAbort then exit;
4086 
4087     // close all loaded files
4088   SourceEditorManager.IncUpdateLock;
4089   try
4090     while SourceEditorManager.SourceEditorCount > 0 do begin
4091       SrcEdit:=SourceEditorManager.SourceEditors[SourceEditorManager.SourceEditorCount-1];
4092       Result:=CloseEditorFile(SrcEdit,[cfProjectClosing]);
4093       if Result=mrAbort then exit;
4094     end;
4095   finally
4096     SourceEditorManager.DecUpdateLock;
4097   end;
4098   // remove all source modifications
4099   CodeToolBoss.SourceCache.ClearAllModified;
4100 
4101   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject B');{$ENDIF}
4102   IncreaseCompilerParseStamp;
4103 
4104   // close Project
4105   if ProjInspector<>nil then
4106     ProjInspector.LazProject:=nil;
4107   FreeThenNil(Project1);
4108   if IDEMessagesWindow<>nil then IDEMessagesWindow.Clear;
4109 
4110   MainIDE.UpdateCaption;
4111   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject C');{$ENDIF}
4112   Result:=mrOk;
4113   //debugln('CloseProject end ',CodeToolBoss.ConsistencyCheck);
4114 end;
4115 
4116 procedure OpenProject(aMenuItem: TIDEMenuItem);
4117 var
4118   OpenDialog: TOpenDialog;
4119   AFileName: string;
4120   LoadFlags: TLoadBufferFlags;
4121   PreReadBuf: TCodeBuffer;
4122   SourceType: String;
4123   LPIFilename: String;
4124 begin
4125   if Assigned(aMenuItem) and (aMenuItem.Section=itmProjectRecentOpen) then
4126   begin
4127     // Hint holds the full filename, Caption may have a shortened form.
4128     AFileName:=aMenuItem.Hint;
4129     Assert(AFileName = ExpandFileNameUTF8(AFileName),'OpenProject: AFileName is not absolute.');
4130     if MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent])=mrOk then begin
4131       AddRecentProjectFile(AFilename);
4132     end else begin
4133       // open failed
4134       if not FileExistsCached(AFilename) then begin
4135         EnvironmentOptions.RemoveFromRecentProjectFiles(AFilename);
4136       end else
4137         AddRecentProjectFile(AFilename);
4138     end;
4139   end
4140   else begin
4141     OpenDialog:=TOpenDialog.Create(nil);
4142     try
4143       InputHistories.ApplyFileDialogSettings(OpenDialog);
4144       OpenDialog.Title:=lisOpenProjectFile+' (*.lpi)';
4145       OpenDialog.Filter := dlgFilterLazarusProject+' (*.lpi)|*.lpi|'
4146                           +dlgFilterAll+'|'+GetAllFilesMask;
4147       if OpenDialog.Execute then begin
4148         AFilename:=GetPhysicalFilenameCached(ExpandFileNameUTF8(OpenDialog.Filename),false);
4149         if CompareFileExt(AFilename,'.lpi')<>0 then begin
4150           // not a lpi file
4151           // check if it is a program source
4152 
4153           // load the source
4154           LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert];
4155           if LoadCodeBuffer(PreReadBuf,AFileName,LoadFlags,true)<>mrOk then exit;
4156 
4157           // check if unit is a program
4158           SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false);
4159           if (SysUtils.CompareText(SourceType,'PROGRAM')=0)
4160           or (SysUtils.CompareText(SourceType,'LIBRARY')=0)
4161           then begin
4162             // source is a program
4163             // either this is a lazarus project
4164             // or it is not yet a lazarus project ;)
4165             LPIFilename:=ChangeFileExt(AFilename,'.lpi');
4166             if FileExistsCached(LPIFilename) then begin
4167               if IDEQuestionDialog(lisProjectInfoFileDetected,
4168                   Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP, [AFilename]),
4169                   mtConfirmation, [mrOk,lisOpenProject2,
4170                                    mrCancel]) <> mrOk
4171               then
4172                 exit;
4173               AFilename:=LPIFilename;
4174             end else begin
4175               if IDEQuestionDialog(lisFileHasNoProject,
4176                 Format(lisTheFileIsNotALazarusProjectCreateANewProjectForThi,
4177                        [AFilename, LineEnding, lowercase(SourceType)]),
4178                 mtConfirmation, [mrYes, lisCreateProject,
4179                                  mrCancel]) <> mrYes
4180               then
4181                 exit;
4182               CreateProjectForProgram(PreReadBuf);
4183               exit;
4184             end;
4185           end;
4186         end;
4187         MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent]);
4188       end;
4189       InputHistories.StoreFileDialogSettings(OpenDialog);
4190     finally
4191       OpenDialog.Free;
4192     end;
4193   end;
4194 end;
4195 
AskToSaveEditorsnull4196 function AskToSaveEditors(EditorList: TList): TModalResult;
4197 // Ask from user about saving the changed SourceEditors in EditorList.
4198 var
4199   Ed: TSourceEditor;
4200   r: TModalResult;
4201   i, Remain: Integer;
4202 begin
4203   Result := mrOK;
4204   if EditorList.Count = 1 then begin
4205     Ed := TSourceEditor(EditorList[0]);
4206     r := IDEQuestionDialog(lisSourceModified,
4207            Format(lisSourceOfPageHasChangedSave, [Ed.PageName]),
4208            mtConfirmation, [mrYes, lisMenuSave,
4209                             mrNo, lisDiscardChanges,
4210                             mrAbort]);
4211     case r of
4212       mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]);
4213       mrNo: ; // don't save
4214       mrAbort, mrCancel:  Result := mrAbort;
4215     end;
4216   end
4217   else if EditorList.Count > 1 then
4218     for i := 0 to EditorList.Count - 1 do begin
4219       Ed := TSourceEditor(EditorList[i]);
4220       Remain := EditorList.Count-i-1;    // Remaining number of files to go.
4221       r := IDEQuestionDialog(lisSourceModified,
4222             Format(lisSourceOfPageHasChangedSaveEx, [Ed.PageName,Remain]),
4223             mtConfirmation, [mrYes, lisMenuSave,
4224                              mrAll, lisSaveAll,
4225                              mrNo, lisDiscardChanges,
4226                              mrIgnore, lisDiscardChangesAll,
4227                              mrAbort]);
4228       case r of
4229         mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]);
4230         mrNo: ; // don't save
4231         mrAll: begin
4232             MainIDE.DoSaveAll([]);
4233             break;
4234           end;
4235         mrIgnore: break; // don't save anymore
4236         mrAbort, mrCancel: begin
4237             Result := mrAbort;
4238             break;
4239           end;
4240       end;
4241     end;
4242 end;
4243 
4244 procedure CloseAll;
4245 // Close editor files
4246 var
4247   Ed: TSourceEditor;
4248   EditorList: TList;
4249   i: Integer;
4250 begin
4251   EditorList := TList.Create;
4252   try
4253     // Collect changed editors into a list and save them after asking from user.
4254     for i := 0 to SourceEditorManager.UniqueSourceEditorCount - 1 do
4255     begin
4256       Ed := TSourceEditor(SourceEditorManager.UniqueSourceEditors[i]);
4257       if CheckEditorNeedsSave(Ed, False) then
4258         EditorList.Add(Ed);
4259     end;
4260     if AskToSaveEditors(EditorList) <> mrOK then Exit;
4261   finally
4262     EditorList.Free;
4263   end;
4264   // Now close them all.
4265   SourceEditorManager.IncUpdateLock;
4266   try
4267     while (SourceEditorManager.SourceEditorCount > 0) and
4268       (CloseEditorFile(SourceEditorManager.SourceEditors[0], []) = mrOk)
4269     do ;
4270   finally
4271     SourceEditorManager.DecUpdateLock;
4272   end;
4273 
4274   // Close packages
4275   PkgBoss.DoCloseAllPackageEditors;
4276 end;
4277 
4278 procedure InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook);
4279 // close all source editors except the clicked
4280 var
4281   Ed: TSourceEditor;
4282   EditorList: TList;
4283   i: Integer;
4284 begin
4285   EditorList := TList.Create;
4286   try
4287     // Collect changed editors, except the active one, into a list and maybe save them.
4288     for i := 0 to SrcNoteBook.EditorCount - 1 do begin
4289       Ed := SrcNoteBook.Editors[i];
4290       if (i <> PageIndex) and CheckEditorNeedsSave(Ed, True) then
4291         EditorList.Add(Ed);
4292     end;
4293     if AskToSaveEditors(EditorList) <> mrOK then Exit;
4294   finally
4295     EditorList.Free;
4296   end;
4297   // Now close all editors except the active one.
4298   SourceEditorManager.IncUpdateLock;
4299   try
4300     repeat
4301       i:=SrcNoteBook.PageCount-1;
4302       if i=PageIndex then dec(i);
4303       if i<0 then break;
4304       if CloseEditorFile(SrcNoteBook.FindSourceEditorWithPageIndex(i),[])<>mrOk then exit;
4305       if i<PageIndex then PageIndex:=i;
4306     until false;
4307   finally
4308     SourceEditorManager.DecUpdateLock;
4309   end;
4310 end;
4311 
4312 function CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor;
4313   NewOwner: TObject; NewFilename: string;
4314   var NewCodeBuffer: TCodeBuffer; var NewUnitName: string): TModalResult;
4315 var
4316   NewShortFilename: String;
4317   NewFileExt: String;
4318   SearchFlags: TSearchIDEFileFlags;
4319 begin
4320   //debugln('CreateNewCodeBuffer START NewFilename=',NewFilename,' ',Descriptor.DefaultFilename,' ',Descriptor.ClassName);
4321   NewUnitName:='';
4322   NewCodeBuffer:=nil;
4323   if NewFilename='' then begin
4324     // create a new unique filename
4325     SearchFlags:=[siffCheckAllProjects];
4326     if Descriptor.IsPascalUnit then begin
4327       if NewUnitName='' then
4328         NewUnitName:=Descriptor.DefaultSourceName;
4329       NewShortFilename:=lowercase(NewUnitName);
4330       NewFileExt:=Descriptor.DefaultFileExt;
4331       SearchFlags:=SearchFlags+[siffIgnoreExtension];
4332     end else begin
4333       NewFilename:=ExtractFilename(Descriptor.DefaultFilename);
4334       NewShortFilename:=ExtractFilenameOnly(NewFilename);
4335       NewFileExt:=ExtractFileExt(NewFilename);
4336       SearchFlags:=[];
4337     end;
4338     NewFilename:=MainIDE.CreateNewUniqueFilename(NewShortFilename,
4339                                            NewFileExt,NewOwner,SearchFlags,true);
4340     if NewFilename='' then
4341       RaiseGDBException('');
4342     NewShortFilename:=ExtractFilenameOnly(NewFilename);
4343     // use as unitname the NewShortFilename, but with the case of the
4344     // original unitname. e.g. 'unit12.pas' becomes 'Unit12.pas'
4345     if Descriptor.IsPascalUnit then begin
4346       NewUnitName:=ChompEndNumber(NewUnitName);
4347       NewUnitName:=NewUnitName+copy(NewShortFilename,length(NewUnitName)+1,
4348                                     length(NewShortFilename));
4349     end;
4350   end;
4351   //debugln('CreateNewCodeBuffer NewFilename=',NewFilename,' NewUnitName=',NewUnitName);
4352 
4353   if FilenameIsPascalUnit(NewFilename) then begin
4354     if NewUnitName='' then
4355       NewUnitName:=ExtractFileNameOnly(NewFilename);
4356     if EnvironmentOptions.CharcaseFileAction in [ccfaAsk, ccfaAutoRename] then
4357       NewFilename:=ExtractFilePath(NewFilename)
4358                    +lowercase(ExtractFileName(NewFilename));
4359   end;
4360 
4361   NewCodeBuffer:=CodeToolBoss.CreateFile(NewFilename);
4362   if NewCodeBuffer=nil then
4363     exit(mrCancel);
4364   Result:=mrOk;
4365 end;
4366 
4367 function CreateNewForm(NewUnitInfo: TUnitInfo;
4368   AncestorType: TPersistentClass; ResourceCode: TCodeBuffer;
4369   UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult;
4370 var
4371   NewComponent: TComponent;
4372   new_x, new_y: integer;
4373   MainIDEBarBottom: integer;
4374   r: TRect;
4375 begin
4376   if not AncestorType.InheritsFrom(TComponent) then
4377     RaiseGDBException('CreateNewForm invalid AncestorType');
4378 
4379   //debugln('CreateNewForm START ',NewUnitInfo.Filename,' ',AncestorType.ClassName,' ',dbgs(ResourceCode<>nil));
4380   // create a buffer for the new resource file and for the LFM file
4381   if ResourceCode=nil then
4382     ResourceCode:=CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename,
4383                                                         ResourceFileExt));
4384   //debugln('CreateNewForm B ',ResourceCode.Filename);
4385   ResourceCode.Source:='{ '+LRSComment+' }';
4386   CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename,'.lfm'));
4387 
4388   // clear formeditor
4389   FormEditor1.ClearSelection;
4390 
4391   // Figure out where we want to put the new form
4392   // if there is more place left of the OI put it left, otherwise right
4393   if ObjectInspector1<>nil then begin
4394     new_x:=ObjectInspector1.Left+10;
4395     new_y:=ObjectInspector1.Top+10;
4396   end else begin
4397     new_x:=200;
4398     new_y:=100;
4399   end;
4400   if new_x>Screen.Width div 2 then
4401     new_x:=new_x-500
4402   else if ObjectInspector1<>nil then
4403     new_x:=new_x + ObjectInspector1.Width + GetSystemMetrics(SM_CXFRAME) shl 1;
4404   if Assigned(MainIDEBar) then
4405   begin
4406     MainIDEBarBottom:=MainIDEBar.Top+MainIDEBar.Height+GetSystemMetrics(SM_CYFRAME) shl 1
4407                                                       +GetSystemMetrics(SM_CYCAPTION);
4408     if MainIDEBarBottom < Screen.Height div 2 then
4409       new_y:=Max(new_y,MainIDEBarBottom+10);
4410   end;
4411   r:=Screen.PrimaryMonitor.WorkareaRect;
4412   new_x:=Max(r.Left,Min(new_x,r.Right-400));
4413   new_y:=Max(r.Top,Min(new_y,r.Bottom-400));
4414 
4415   // create jit component
4416   NewComponent := FormEditor1.CreateComponent(nil,TComponentClass(AncestorType),
4417       NewUnitInfo.CreateUnitName, new_x, new_y, 0,0,DisableAutoSize);
4418   if NewComponent=nil then begin
4419     DebugLn(['CreateNewForm FormEditor1.CreateComponent failed ',dbgsName(TComponentClass(AncestorType))]);
4420     exit(mrCancel);
4421   end;
4422   FormEditor1.SetComponentNameAndClass(NewComponent,
4423     NewUnitInfo.ComponentName,'T'+NewUnitInfo.ComponentName);
4424   if NewComponent is TCustomForm then
4425     TControl(NewComponent).Visible := False;
4426   if (NewComponent is TControl)
4427   and (csSetCaption in TControl(NewComponent).ControlStyle) then
4428     TControl(NewComponent).Caption:=NewComponent.Name;
4429   NewUnitInfo.Component := NewComponent;
4430   MainIDE.CreateDesignerForComponent(NewUnitInfo,NewComponent);
4431   if NewComponent is TCustomDesignControl then
4432   begin
4433     TCustomDesignControl(NewComponent).DesignTimePPI := Screen.PixelsPerInch;
4434     TCustomDesignControl(NewComponent).PixelsPerInch := Screen.PixelsPerInch;
4435   end;
4436 
4437   NewUnitInfo.ComponentName:=NewComponent.Name;
4438   NewUnitInfo.ComponentResourceName:=NewUnitInfo.ComponentName;
4439   if UseCreateFormStatements and (NewComponent is TCustomForm)
4440   and NewUnitInfo.IsPartOfProject
4441   and Project1.AutoCreateForms
4442   and (pfMainUnitHasCreateFormStatements in Project1.Flags) then
4443   begin
4444     Project1.AddCreateFormToProjectFile(NewComponent.ClassName,
4445                                         NewComponent.Name);
4446   end;
4447   Result:=mrOk;
4448 end;
4449 
4450 function NewUniqueComponentName(Prefix: string): string;
4451 
4452   function SearchProject(AProject: TProject; const Identifier: string): boolean;
4453   var
4454     i: Integer;
4455     AnUnitInfo: TUnitInfo;
4456   begin
4457     if AProject=nil then exit(false);
4458     Result:=true;
4459     for i:=0 to AProject.UnitCount-1 do
4460     begin
4461       AnUnitInfo:=AProject.Units[i];
4462       if (AnUnitInfo.Component<>nil) then begin
4463         if CompareText(AnUnitInfo.Component.Name,Identifier)=0 then exit;
4464         if CompareText(AnUnitInfo.Component.ClassName,Identifier)=0 then exit;
4465       end else if (AnUnitInfo.ComponentName<>'')
4466       and ((AnUnitInfo.IsPartOfProject) or AnUnitInfo.Loaded) then begin
4467         if SysUtils.CompareText(AnUnitInfo.Unit_Name,Identifier)=0 then exit;
4468         if SysUtils.CompareText(AnUnitInfo.ComponentName,Identifier)=0 then exit;
4469       end;
4470     end;
4471     Result:=false;
4472   end;
4473 
4474   function SearchPackage(APackage: TLazPackage; const Identifier: string): boolean;
4475   var
4476     i: Integer;
4477     PkgFile: TPkgFile;
4478   begin
4479     if APackage=nil then exit(false);
4480     Result:=true;
4481     if SysUtils.CompareText(APackage.Name,Identifier)=0 then exit;
4482     for i:=0 to APackage.FileCount-1 do
4483     begin
4484       PkgFile:=APackage.Files[i];
4485       if SysUtils.CompareText(PkgFile.Unit_Name,Identifier)=0 then exit;
4486     end;
4487     Result:=false;
4488   end;
4489 
4490   function IdentifierExists(Identifier: string): boolean;
4491   var
4492     i: Integer;
4493   begin
4494     Result:=true;
4495     if GetClass(Identifier)<>nil then exit;
4496     if SearchProject(Project1,Identifier) then exit;
4497     for i:=0 to PackageGraph.Count-1 do
4498       if SearchPackage(PackageGraph[i],Identifier) then exit;
4499     Result:=false;
4500   end;
4501 
4502   function IdentifierIsOk(Identifier: string): boolean;
4503   begin
4504     Result:=false;
4505     if not IsValidIdent(Identifier) then exit;
4506     if AllKeyWords.DoIdentifier(PChar(Identifier)) then exit;
4507     if IdentifierExists(Identifier) then exit;
4508     if IdentifierExists('T'+Identifier) then exit;
4509     Result:=true;
4510   end;
4511 
4512 var
4513   i: Integer;
4514 begin
4515   if IdentifierIsOk(Prefix) then
4516     exit(Prefix);
4517   while (Prefix<>'') and (Prefix[length(Prefix)] in ['0'..'9']) do
4518     System.Delete(Prefix,length(Prefix),1);
4519   if not IsValidIdent(Prefix) then
4520     Prefix:='Resource';
4521   i:=0;
4522   repeat
4523     inc(i);
4524     Result:=Prefix+IntToStr(i);
4525   until IdentifierIsOk(Result);
4526 end;
4527 
4528 function ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo;
4529   var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult;
4530 var
4531   SaveDialog: TIDESaveDialog;
4532   SrcEdit: TSourceEditor;
4533   SaveAsFilename, SaveAsFileExt: string;
4534   NewFilename, NewFileExt: string;
4535   OldUnitName, NewUnitName: string;
4536   ACaption, AText, APath: string;
4537   Filter, AllEditorExt, AllFilter: string;
4538 begin
4539   if (AnUnitInfo<>nil) and (AnUnitInfo.OpenEditorInfoCount>0) then
4540     SrcEdit := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent)
4541   else
4542     SrcEdit:=nil;
4543   //debugln('ShowSaveFileAsDialog ',AnUnitInfo.Filename);
4544 
4545   // try to keep the old filename and extension
4546   SaveAsFileExt:=ExtractFileExt(AFileName);
4547   if (SaveAsFileExt='') and (SrcEdit<>nil) then begin
4548     if (SrcEdit.SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then
4549       SaveAsFileExt:=PascalExtension[EnvironmentOptions.PascalFileExtension]
4550     else
4551       SaveAsFileExt:=EditorOpts.HighlighterList.GetDefaultFilextension(
4552                          SrcEdit.SyntaxHighlighterType);
4553   end;
4554   if FilenameIsPascalSource(AFilename) then begin
4555     if AnUnitInfo<>nil then
4556       OldUnitName:=AnUnitInfo.ReadUnitNameFromSource(false)
4557     else
4558       OldUnitName:=ExtractFileNameOnly(AFilename);
4559   end else
4560     OldUnitName:='';
4561   //debugln('ShowSaveFileAsDialog sourceunitname=',OldUnitName);
4562   SaveAsFilename:=OldUnitName;
4563   if SaveAsFilename='' then
4564     SaveAsFilename:=ExtractFileNameOnly(AFilename);
4565   if SaveAsFilename='' then
4566     SaveAsFilename:=lisnoname;
4567 
4568   //suggest lowercased name if user wants so
4569   if EnvironmentOptions.LowercaseDefaultFilename = true then
4570     SaveAsFilename:=LowerCase(SaveAsFilename);
4571 
4572   // let user choose a filename
4573   SaveDialog:=IDESaveDialogClass.Create(nil);
4574   try
4575     InputHistories.ApplyFileDialogSettings(SaveDialog);
4576     SaveDialog.Title:=lisSaveSpace+SaveAsFilename+' (*'+SaveAsFileExt+')';
4577     SaveDialog.FileName:=SaveAsFilename+SaveAsFileExt;
4578 
4579     Filter := dlgFilterLazarusUnit + ' (*.pas;*.pp)|*.pas;*.pp';
4580     if (SaveAsFileExt='.lpi') then
4581       Filter:=Filter+ '|' + dlgFilterLazarusProject + ' (*.lpi)|*.lpi';
4582     if (SaveAsFileExt='.lfm') or (SaveAsFileExt='.dfm') then
4583       Filter:=Filter+ '|' + dlgFilterLazarusForm + ' (*.lfm;*.dfm)|*.lfm;*.dfm';
4584     if (SaveAsFileExt='.lpk') then
4585       Filter:=Filter+ '|' + dlgFilterLazarusPackage + ' (*.lpk)|*.lpk';
4586     if (SaveAsFileExt='.lpr') then
4587       Filter:=Filter+ '|' + dlgFilterLazarusProjectSource + ' (*.lpr)|*.lpr';
4588     // append a filter for all editor files
4589     CreateFileDialogFilterForSourceEditorFiles(Filter,AllEditorExt,AllFilter);
4590     if AllEditorExt<>'' then
4591       Filter:=Filter+ '|' + dlgFilterLazarusEditorFile + ' (' + AllEditorExt + ')|' + AllEditorExt;
4592 
4593     // append an any file filter *.*
4594     Filter:=Filter+ '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
4595 
4596     // prepend an all filter
4597     Filter:=  dlgFilterLazarusFile + ' ('+AllFilter+')|' + AllFilter + '|' + Filter;
4598     SaveDialog.Filter := Filter;
4599 
4600     // if this is a project file, start in project directory
4601     if (AnUnitInfo=nil)
4602     or (AnUnitInfo.IsPartOfProject and (not Project1.IsVirtual)
4603         and (not PathIsInPath(SaveDialog.InitialDir,Project1.Directory)))
4604     then begin
4605       SaveDialog.InitialDir:=Project1.Directory;
4606     end;
4607     // if this is a package file, then start in package directory
4608     APath:=PkgBoss.GetDefaultSaveDirectoryForFile(AFilename);
4609     if (APath<>'') and (not PathIsInPath(SaveDialog.InitialDir,APath)) then
4610       SaveDialog.InitialDir:=APath;
4611 
4612     repeat
4613       Result:=mrCancel;
4614       // show save dialog
4615       if (not SaveDialog.Execute) or (ExtractFileName(SaveDialog.Filename)='') then
4616         exit;  // user cancels
4617       NewFilename:=ExpandFileNameUTF8(SaveDialog.Filename);
4618 
4619       // check file extension
4620       NewFileExt:=ExtractFileExt(NewFilename);
4621       if NewFileExt='' then begin
4622         NewFileExt:=SaveAsFileExt;
4623         NewFilename:=NewFilename+SaveAsFileExt;
4624       end;
4625 
4626       // check file path
4627       APath:=ExtractFilePath(NewFilename);
4628       if not DirPathExists(APath) then begin
4629         ACaption:=lisEnvOptDlgDirectoryNotFound;
4630         AText:=Format(lisTheDestinationDirectoryDoesNotExist, [LineEnding, APath]);
4631         Result:=IDEMessageDialogAb(ACaption, AText, mtConfirmation,[mbCancel],CanAbort);
4632         exit;
4633       end;
4634 
4635       // check unitname
4636       if (NewFileExt<>'') and IsPascalUnitExt(PChar(NewFileExt)) then begin
4637         NewUnitName:=ExtractFileNameOnly(NewFilename);
4638         // Do not rename the unit if new filename differs from its name only in case
4639         if LowerCase(OldUnitName)=NewUnitName then
4640           NewUnitName:=OldUnitName;
4641         if NewUnitName='' then
4642           exit(mrCancel);
4643         // Is it a valid name? Ask user.
4644         if not IsValidUnitName(NewUnitName) then
4645         begin
4646           Result:=IDEQuestionDialogAb(lisInvalidPascalIdentifierCap,
4647               Format(lisInvalidPascalIdentifierName,[NewUnitName,LineEnding]),
4648               mtConfirmation, [mrIgnore, lisSave,
4649                                mrCancel, lisCancel,
4650                                mrRetry, lisChooseADifferentName,
4651                                mrAbort, lisAbort], not CanAbort);
4652           if Result=mrRetry then
4653             continue;
4654           if Result in [mrCancel,mrAbort] then
4655             exit;
4656         end;
4657         // Does the project already have such unit?
4658         if Project1.IndexOfUnitWithName(NewUnitName,true,AnUnitInfo)>=0 then
4659         begin
4660           Result:=IDEQuestionDialogAb(lisUnitNameAlreadyExistsCap,
4661               Format(lisTheUnitAlreadyExists, [NewUnitName]),
4662               mtConfirmation, [mrIgnore, lisForceRenaming,
4663                                mrCancel, lisCancelRenaming,
4664                                mrAbort, lisAbort], not CanAbort);
4665           if Result<>mrIgnore then
4666             exit;
4667         end;
4668       end;
4669     until Result<>mrRetry;
4670   finally
4671     InputHistories.StoreFileDialogSettings(SaveDialog);
4672     SaveDialog.Free;
4673   end;
4674 
4675   // check filename
4676   if FilenameIsPascalUnit(NewFilename) then begin
4677     AText:=ExtractFileName(NewFilename);
4678     // check if file should be auto renamed
4679     if EnvironmentOptions.CharcaseFileAction = ccfaAsk then begin
4680       if LowerCase(AText)<>AText then begin
4681         Result:=IDEQuestionDialogAb(lisRenameFile,
4682             Format(lisThisLooksLikeAPascalFileItIsRecommendedToUseLowerC,
4683                    [LineEnding, LineEnding]),
4684             mtWarning, [mrYes, lisRenameToLowercase,
4685                         mrNo, lisKeepName,
4686                         mrAbort, lisAbort], not CanAbort);
4687         case Result of
4688         mrYes: NewFileName:=ExtractFilePath(NewFilename)+lowercase(AText);
4689         mrAbort, mrCancel: exit;
4690         end;
4691         Result:=mrOk;
4692       end;
4693     end else begin
4694       if EnvironmentOptions.CharcaseFileAction = ccfaAutoRename then
4695         NewFileName:=ExtractFilePath(NewFilename)+LowerCase(AText);
4696     end;
4697   end;
4698 
4699   // check overwrite existing file
4700   if IDESaveDialogClass.NeedOverwritePrompt
4701       and ((not FilenameIsAbsolute(AFilename))
4702           or (CompareFilenames(NewFilename,AFilename)<>0))
4703   and FileExistsUTF8(NewFilename) then
4704   begin
4705     ACaption:=lisOverwriteFile;
4706     AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewFilename, LineEnding]);
4707     Result:=IDEQuestionDialogAb(ACaption, AText, mtConfirmation,
4708                                 [mrYes, lisOverwriteFileOnDisk,
4709                                  mrCancel,
4710                                  mrAbort, lisAbort], not CanAbort);
4711     if Result=mrCancel then exit;
4712   end;
4713 
4714   if AnUnitInfo<>nil then begin
4715     // rename unit
4716     Result:=RenameUnit(AnUnitInfo,NewFilename,NewUnitName,LFMCode,LRSCode);
4717     AFilename:=AnUnitInfo.Filename;
4718     if Result<>mrOk then exit;
4719   end else begin
4720     Result:=mrOk;
4721     AFilename:=NewFilename;
4722   end;
4723 end;
4724 
4725 type
4726   TTranslateStringItem = record
4727     Name: String;
4728     Value: String;
4729   end;
4730 
4731   TTranslateStrings = class
4732   private
4733     FList: array of TTranslateStringItem;
4734     function CalcHash(const S: string): Cardinal;
4735     function GetSourceBytes(const S: string): string;
4736     function GetValue(const S: string): string;
4737   public
4738     destructor Destroy; override;
4739     procedure Add(const AName, AValue: String);
4740     function Count: Integer;
4741     function Text: String;
4742   end;
4743 
4744   TLRJGrubber = class(TObject)
4745   private
4746     FGrubbed: TTranslateStrings;
4747     FWriter: TWriter;
4748   public
4749     constructor Create(TheWriter: TWriter);
4750     destructor Destroy; override;
4751     procedure Grub(Sender: TObject; const Instance: TPersistent;
4752                    PropInfo: PPropInfo; var Content: string);
4753     property Grubbed: TTranslateStrings read FGrubbed;
4754     property Writer: TWriter read FWriter write FWriter;
4755   end;
4756 
CalcHashnull4757 function TTranslateStrings.CalcHash(const S: string): Cardinal;
4758 var
4759   g: Cardinal;
4760   i: Longint;
4761 begin
4762   Result:=0;
4763   for i:=1 to Length(s) do
4764   begin
4765     Result:=Result shl 4;
4766     inc(Result,Ord(S[i]));
4767     g:=Result and ($f shl 28);
4768     if g<>0 then
4769      begin
4770        Result:=Result xor (g shr 24);
4771        Result:=Result xor g;
4772      end;
4773   end;
4774   If Result=0 then
4775     Result:=$ffffffff;
4776 end;
4777 
GetSourceBytesnull4778 function TTranslateStrings.GetSourceBytes(const S: string): string;
4779 var
4780   i, l: Integer;
4781 begin
4782   Result:='';
4783   l:=Length(S);
4784   for i:=1 to l do
4785   begin
4786     Result:=Result+IntToStr(Ord(S[i]));
4787     if i<>l then
4788      Result:=Result+',';
4789   end;
4790 end;
4791 
GetValuenull4792 function TTranslateStrings.GetValue(const S: string): string;
4793 var
4794   i, l: Integer;
4795   jsonstr: unicodestring;
4796 begin
4797   Result:='';
4798   //input string is assumed to be in UTF-8 encoding
4799   jsonstr:=UTF8ToUTF16(StringToJSONString(S));
4800   l:=Length(jsonstr);
4801   for i:=1 to l do
4802   begin
4803     if (Ord(jsonstr[i])<32) or (Ord(jsonstr[i])>=127) then
4804       Result:=Result+'\u'+HexStr(Ord(jsonstr[i]), 4)
4805     else
4806       Result:=Result+Char(jsonstr[i]);
4807   end;
4808 end;
4809 
4810 destructor TTranslateStrings.Destroy;
4811 begin
4812   SetLength(FList,0);
4813 end;
4814 
4815 procedure TTranslateStrings.Add(const AName, AValue: String);
4816 begin
4817   SetLength(FList,Length(FList)+1);
4818   with FList[High(FList)] do
4819   begin
4820     Name:=AName;
4821     Value:=AValue;
4822   end;
4823 end;
4824 
Countnull4825 function TTranslateStrings.Count: Integer;
4826 begin
4827   Result:=Length(FList);
4828 end;
4829 
Textnull4830 function TTranslateStrings.Text: String;
4831 var
4832   i: Integer;
4833   R: TTranslateStringItem;
4834 begin
4835   Result:='';
4836   if Length(FList)=0 then Exit;
4837   Result:='{"version":1,"strings":['+LineEnding;
4838   for i:=Low(FList) to High(FList) do
4839   begin
4840     R:=TTranslateStringItem(FList[i]);
4841     Result:=Result+'{"hash":'+IntToStr(CalcHash(R.Value))+',"name":"'+R.Name+
4842       '","sourcebytes":['+GetSourceBytes(R.Value)+
4843       '],"value":"'+GetValue(R.Value)+'"}';
4844     if i<High(FList) then
4845       Result:=Result+','+LineEnding
4846     else
4847       Result:=Result+LineEnding;
4848   end;
4849   Result:=Result+']}'+LineEnding;
4850 end;
4851 
4852 constructor TLRJGrubber.Create(TheWriter: TWriter);
4853 begin
4854   inherited Create;
4855   FGrubbed:=TTranslateStrings.Create;
4856   FWriter:=TheWriter;
4857   FWriter.OnWriteStringProperty:=@Grub;
4858 end;
4859 
4860 destructor TLRJGrubber.Destroy;
4861 begin
4862   FGrubbed.Free;
4863   inherited Destroy;
4864 end;
4865 
4866 procedure TLRJGrubber.Grub(Sender: TObject; const Instance: TPersistent;
4867   PropInfo: PPropInfo; var Content: string);
4868 var
4869   LRSWriter: TLRSObjectWriter;
4870   Path: String;
4871 begin
4872   if not Assigned(Instance) then exit;
4873   if not Assigned(PropInfo) then exit;
4874   if SysUtils.CompareText(PropInfo^.PropType^.Name,'TTRANSLATESTRING')<>0 then exit;
4875   if (SysUtils.CompareText(Instance.ClassName,'TMENUITEM')=0) and (Content='-') then exit;
4876   if Writer.Driver is TLRSObjectWriter then begin
4877     LRSWriter:=TLRSObjectWriter(Writer.Driver);
4878     Path:=LRSWriter.GetStackPath;
4879   end else begin
4880     Path:=Instance.ClassName+'.'+PropInfo^.Name;
4881   end;
4882   FGrubbed.Add(LowerCase(Path),Content);
4883 end;
4884 
4885 function SaveUnitComponent(AnUnitInfo: TUnitInfo;
4886   LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult;
4887 
4888   function IsI18NEnabled(UnitOwners: TFPList): boolean;
4889   var
4890     i: Integer;
4891     APackage: TLazPackage;
4892     PkgFile: TPkgFile;
4893   begin
4894     if AnUnitInfo.IsPartOfProject then begin
4895       // a project unit
4896       Result:=AnUnitInfo.Project.EnableI18N and AnUnitInfo.Project.EnableI18NForLFM
4897          and (not AnUnitInfo.DisableI18NForLFM);
4898       exit;
4899     end;
4900     if (UnitOwners<>nil) then begin
4901       for i:=0 to UnitOwners.Count-1 do begin
4902         if TObject(UnitOwners[i]) is TLazPackage then begin
4903           // a package unit
4904           APackage:=TLazPackage(UnitOwners[i]);
4905           Result:=false;
4906           if APackage.EnableI18N and APackage.EnableI18NForLFM then begin
4907             PkgFile:=APackage.FindPkgFile(AnUnitInfo.Filename,true,true);
4908             Result:=(PkgFile<>nil) and (not PkgFile.DisableI18NForLFM);
4909           end;
4910           exit;
4911         end;
4912       end;
4913     end;
4914     // a rogue unit
4915     Result:=false;
4916   end;
4917 
4918 var
4919   ComponentSavingOk: boolean;
4920   MemStream, BinCompStream, TxtCompStream: TExtMemoryStream;
4921   DestroyDriver: Boolean;
4922   Writer: TWriter;
4923   ACaption, AText: string;
4924   CompResourceCode, LFMFilename, TestFilename: string;
4925   ADesigner: TIDesigner;
4926   Grubber: TLRJGrubber;
4927   LRJFilename: String;
4928   AncestorUnit: TUnitInfo;
4929   Ancestor: TComponent;
4930   HasI18N: Boolean;
4931   UnitOwners: TFPList;
4932   LRSFilename: String;
4933   PropPath: String;
4934   ResType: TResourceType;
4935 begin
4936   Result:=mrCancel;
4937 
4938   // save lrs - lazarus resource file and lfm - lazarus form text file
4939   // Note: When there is a bug in the source, the include directive of the
4940   //       resource code can not be found, therefore the LFM file should always
4941   //       be saved first.
4942   //       And therefore each TUnitInfo stores the resource filename (.lrs).
4943 
4944   // the lfm file is saved before the lrs file, because the IDE only needs the
4945   // lfm file to recreate the lrs file.
4946   // by VVI - now a LRT file is saved in addition to LFM and LRS
4947   // LRT file format (in present) are lines
4948   // <ClassName>.<PropertyName>=<PropertyValue>
4949   LRSFilename:='';
4950   ResType:=MainBuildBoss.GetResourceType(AnUnitInfo);
4951   LRSCode:=nil;
4952 
4953   if (AnUnitInfo.Component<>nil) then begin
4954     // stream component to resource code and to lfm file
4955     ComponentSavingOk:=true;
4956 
4957     // clean up component
4958     Result:=RemoveLooseEvents(AnUnitInfo);
4959     if Result<>mrOk then exit;
4960 
4961     // save designer form properties to the component
4962     FormEditor1.SaveHiddenDesignerFormProperties(AnUnitInfo.Component);
4963 
4964     if ResType=rtLRS then begin
4965       if (sfSaveToTestDir in Flags) then
4966         LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo)
4967       else
4968         LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true);
4969     end;
4970 
4971     // stream component to binary stream
4972     BinCompStream:=TExtMemoryStream.Create;
4973     if AnUnitInfo.ComponentLastBinStreamSize>0 then
4974       BinCompStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+LRSStreamChunkSize;
4975     Writer:=nil;
4976     DestroyDriver:=false;
4977     Grubber:=nil;
4978     UnitOwners:=nil;
4979     try
4980       UnitOwners:=PkgBoss.GetOwnersOfUnit(AnUnitInfo.Filename);
4981       Result:=mrOk;
4982       repeat
4983         try
4984           BinCompStream.Position:=0;
4985           Writer:=AnUnitInfo.UnitResourceFileformat.CreateWriter(BinCompStream,DestroyDriver);
4986           // used to save lrj files
4987           HasI18N:=IsI18NEnabled(UnitOwners);
4988           if HasI18N then
4989             Grubber:=TLRJGrubber.Create(Writer);
4990           Writer.OnWriteMethodProperty:=@FormEditor1.WriteMethodPropertyEvent;
4991           //DebugLn(['SaveUnitComponent AncestorInstance=',dbgsName(AncestorInstance)]);
4992           Writer.OnFindAncestor:=@FormEditor1.WriterFindAncestor;
4993           AncestorUnit:=AnUnitInfo.FindAncestorUnit;
4994           Ancestor:=nil;
4995           if AncestorUnit<>nil then
4996             Ancestor:=AncestorUnit.Component;
4997           //DebugLn(['SaveUnitComponent Writer.WriteDescendent ARoot=',AnUnitInfo.Component,' Ancestor=',DbgSName(Ancestor)]);
4998           Writer.WriteDescendent(AnUnitInfo.Component,Ancestor);
4999           if DestroyDriver then
5000             Writer.Driver.Free;
5001           FreeAndNil(Writer);
5002           AnUnitInfo.ComponentLastBinStreamSize:=BinCompStream.Size;
5003         except
5004           on E: Exception do begin
5005             PropPath:='';
5006             if Writer.Driver is TLRSObjectWriter then
5007               PropPath:=TLRSObjectWriter(Writer.Driver).GetStackPath;
5008             DumpExceptionBackTrace;
5009             ACaption:=lisStreamingError;
5010             AText:=Format(lisUnableToStreamT, [AnUnitInfo.ComponentName,
5011                           AnUnitInfo.ComponentName]) + LineEnding + E.Message;
5012             if PropPath<>'' then
5013               AText := Atext + LineEnding + LineEnding + lisPathToInstance
5014                      + LineEnding + PropPath;
5015             Result:=IDEMessageDialog(ACaption, AText, mtError,
5016                        [mbAbort, mbRetry, mbIgnore]);
5017             if Result=mrAbort then exit;
5018             if Result=mrIgnore then Result:=mrOk;
5019             ComponentSavingOk:=false;
5020           end;
5021         end;
5022       until Result<>mrRetry;
5023 
5024       // create lazarus form resource code
5025       if ComponentSavingOk and (LRSFilename<>'') then begin
5026         if LRSCode=nil then begin
5027           LRSCode:=CodeToolBoss.CreateFile(LRSFilename);
5028           ComponentSavingOk:=(LRSCode<>nil);
5029         end;
5030         if ComponentSavingOk then begin
5031           // there is no bug in the source, so the resource code should be changed too
5032           MemStream:=TExtMemoryStream.Create;
5033           if AnUnitInfo.ComponentLastLRSStreamSize>0 then
5034             MemStream.Capacity:=AnUnitInfo.ComponentLastLRSStreamSize+LRSStreamChunkSize;
5035           try
5036             BinCompStream.Position:=0;
5037             BinaryToLazarusResourceCode(BinCompStream,MemStream
5038               ,'T'+AnUnitInfo.ComponentName,'FORMDATA');
5039             AnUnitInfo.ComponentLastLRSStreamSize:=MemStream.Size;
5040             MemStream.Position:=0;
5041             SetLength(CompResourceCode,MemStream.Size);
5042             MemStream.Read(CompResourceCode[1],length(CompResourceCode));
5043           finally
5044             MemStream.Free;
5045           end;
5046         end;
5047         if ComponentSavingOk then begin
5048           {$IFDEF IDE_DEBUG}
5049           debugln('SaveUnitComponent E ',CompResourceCode);
5050           {$ENDIF}
5051           // replace lazarus form resource code in include file (.lrs)
5052           if not (sfSaveToTestDir in Flags) then begin
5053             // if resource name has changed, delete old resource
5054             if (AnUnitInfo.ComponentName<>AnUnitInfo.ComponentResourceName)
5055             and (AnUnitInfo.ComponentResourceName<>'') then begin
5056               CodeToolBoss.RemoveLazarusResource(LRSCode,
5057                                           'T'+AnUnitInfo.ComponentResourceName);
5058             end;
5059             // add comment to resource file (if not already exists)
5060             if (not CodeToolBoss.AddLazarusResourceHeaderComment(LRSCode,LRSComment)) then
5061             begin
5062               ACaption:=lisResourceSaveError;
5063               AText:=Format(lisUnableToAddResourceHeaderCommentToResourceFile, [
5064                 LineEnding, LRSCode.FileName, LineEnding]);
5065               Result:=IDEMessageDialog(ACaption,AText,mtError,[mbIgnore,mbAbort]);
5066               if Result<>mrIgnore then exit;
5067             end;
5068             // add resource to resource file
5069             if (not CodeToolBoss.AddLazarusResource(LRSCode,
5070                'T'+AnUnitInfo.ComponentName,CompResourceCode)) then
5071             begin
5072               ACaption:=lisResourceSaveError;
5073               AText:=Format(lisUnableToAddResourceTFORMDATAToResourceFileProbably,
5074                 [AnUnitInfo.ComponentName, LineEnding, LRSCode.FileName, LineEnding] );
5075               Result:=IDEMessageDialog(ACaption, AText, mtError, [mbIgnore, mbAbort]);
5076               if Result<>mrIgnore then exit;
5077             end else begin
5078               AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName;
5079             end;
5080           end else begin
5081             LRSCode.Source:=CompResourceCode;
5082           end;
5083         end;
5084       end;
5085       if ComponentSavingOk then begin
5086         if (not AnUnitInfo.IsVirtual) or (sfSaveToTestDir in Flags) then
5087         begin
5088           // save lfm file
5089           LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,false);
5090           if AnUnitInfo.IsVirtual then
5091             LFMFilename:=AppendPathDelim(MainBuildBoss.GetTestBuildDirectory)+LFMFilename;
5092           if LFMCode=nil then begin
5093             LFMCode:=CodeToolBoss.CreateFile(LFMFilename);
5094             if LFMCode=nil then begin
5095               Result:=IDEQuestionDialog(lisUnableToCreateFile,
5096                 Format(lisUnableToCreateFile2, [LFMFilename]),
5097                 mtWarning, [mrIgnore, lisContinueWithoutLoadingForm,
5098                            mrCancel, lisCancelLoadingUnit,
5099                            mrAbort, lisAbortAllLoading]);
5100               if Result<>mrIgnore then exit;
5101             end;
5102           end;
5103           if (LFMCode<>nil) then begin
5104             {$IFDEF IDE_DEBUG}
5105             debugln('SaveUnitComponent E2 LFM=',LFMCode.Filename);
5106             {$ENDIF}
5107             if (ResType=rtRes) and (LFMCode.DiskEncoding<>EncodingUTF8) then
5108             begin
5109               // the .lfm file is used by fpcres, which only supports UTF8 without BOM
5110               DebugLn(['SaveUnitComponent fixing encoding of ',LFMCode.Filename,' from ',LFMCode.DiskEncoding,' to ',EncodingUTF8]);
5111               LFMCode.DiskEncoding:=EncodingUTF8;
5112             end;
5113 
5114             Result:=mrOk;
5115             repeat
5116               try
5117                 // transform binary to text
5118                 TxtCompStream:=TExtMemoryStream.Create;
5119                 if AnUnitInfo.ComponentLastLFMStreamSize>0 then
5120                   TxtCompStream.Capacity:=AnUnitInfo.ComponentLastLFMStreamSize
5121                                           +LRSStreamChunkSize;
5122                 try
5123                   BinCompStream.Position:=0;
5124                   AnUnitInfo.UnitResourceFileformat.BinStreamToTextStream(BinCompStream,TxtCompStream);
5125                   AnUnitInfo.ComponentLastLFMStreamSize:=TxtCompStream.Size;
5126                   // stream text to file
5127                   TxtCompStream.Position:=0;
5128                   LFMCode.LoadFromStream(TxtCompStream);
5129                   Result:=SaveCodeBufferToFile(LFMCode,LFMCode.Filename,true);
5130                   if not Result=mrOk then exit;
5131                   Result:=mrCancel;
5132                 finally
5133                   TxtCompStream.Free;
5134                 end;
5135               except
5136                 on E: Exception do begin
5137                   // added to get more feedback on issue 7009
5138                   Debugln('SaveFileResources E3: ', E.Message);
5139                   DumpExceptionBackTrace;
5140                   ACaption:=lisStreamingError;
5141                   AText:=Format(
5142                     lisUnableToTransformBinaryComponentStreamOfTIntoText, [
5143                     AnUnitInfo.ComponentName, AnUnitInfo.ComponentName])
5144                     +LineEnding+E.Message;
5145                   Result:=IDEMessageDialog(ACaption, AText, mtError,
5146                                      [mbAbort, mbRetry, mbIgnore]);
5147                   if Result=mrAbort then exit;
5148                   if Result=mrIgnore then Result:=mrOk;
5149                 end;
5150               end;
5151             until Result<>mrRetry;
5152           end;
5153         end;
5154       end;
5155       // Now the most important file (.lfm) is saved.
5156       // Now save the secondary files
5157 
5158       // save the .lrj file containing the list of all translatable strings of
5159       // the component
5160       if ComponentSavingOk
5161       and (Grubber<>nil) and (Grubber.Grubbed.Count>0)
5162       and (not (sfSaveToTestDir in Flags))
5163       and (not AnUnitInfo.IsVirtual) then begin
5164         LRJFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lrj');
5165         DebugLn(['SaveUnitComponent save lrj: ',LRJFilename]);
5166         Result:=SaveStringToFile(LRJFilename,Grubber.Grubbed.Text,
5167                                  [mbIgnore,mbAbort],AnUnitInfo.Filename);
5168         if (Result<>mrOk) and (Result<>mrIgnore) then exit;
5169       end;
5170 
5171     finally
5172       try
5173         FreeAndNil(BinCompStream);
5174         if DestroyDriver and (Writer<>nil) then Writer.Driver.Free;
5175         FreeAndNil(Writer);
5176         FreeAndNil(Grubber);
5177         FreeAndNil(UnitOwners);
5178       except
5179         on E: Exception do begin
5180           debugln('SaveUnitComponent Error cleaning up: ',E.Message);
5181         end;
5182       end;
5183     end;
5184   end;
5185   {$IFDEF IDE_DEBUG}
5186   if ResourceCode<>nil then
5187     debugln('SaveUnitComponent F ',ResourceCode.Modified);
5188   {$ENDIF}
5189   // save binary stream (.lrs)
5190   if LRSCode<>nil then begin
5191     if (not (sfSaveToTestDir in Flags)) then
5192     begin
5193       if (LRSCode.Modified) then begin
5194         if FilenameIsAbsolute(LRSCode.Filename) then
5195           LRSFilename:=LRSCode.Filename
5196         else if LRSFilename='' then
5197           LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true);
5198         if (LRSFilename<>'') and FilenameIsAbsolute(LRSFilename) then
5199         begin
5200           Result:=ForceDirectoryInteractive(ExtractFilePath(LRSFilename),[mbRetry]);
5201           if not Result=mrOk then exit;
5202           Result:=SaveCodeBufferToFile(LRSCode,LRSFilename);
5203           if not Result=mrOk then exit;
5204         end;
5205       end;
5206     end else begin
5207       TestFilename:=MainBuildBoss.GetTestUnitFilename(AnUnitInfo);
5208       LRSFilename:=ChangeFileExt(TestFilename,ExtractFileExt(LRSCode.Filename));
5209       Result:=SaveCodeBufferToFile(LRSCode,LRSFilename);
5210       if not Result=mrOk then exit;
5211     end;
5212   end;
5213   // mark designer unmodified
5214   ADesigner:=FindRootDesigner(AnUnitInfo.Component);
5215   if ADesigner<>nil then
5216     ADesigner.DefaultFormBoundsValid:=false;
5217 
5218   Result:=mrOk;
5219   {$IFDEF IDE_DEBUG}
5220   debugln('SaveUnitComponent G ',LFMCode<>nil);
5221   {$ENDIF}
5222 end;
5223 
5224 function RemoveLooseEvents(AnUnitInfo: TUnitInfo): TModalResult;
5225 var
5226   ComponentModified: boolean;
5227   ActiveSrcEdit: TSourceEditor;
5228   ActiveUnitInfo: TUnitInfo;
5229 begin
5230   Result:=mrOk;
5231   if (AnUnitInfo.Component=nil) then exit;
5232   ActiveSrcEdit:=nil;
5233   if not MainIDE.BeginCodeTool(ActiveSrcEdit,ActiveUnitInfo,[]) then exit;
5234   // unselect methods in ObjectInspector1
5235   if (ObjectInspector1<>nil)
5236   and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then
5237   begin
5238     ObjectInspector1.EventGrid.ItemIndex:=-1;
5239     ObjectInspector1.FavoriteGrid.ItemIndex:=-1;
5240   end;
5241   //debugln('RemoveLooseEvents ',AnUnitInfo.Filename,' ',dbgsName(AnUnitInfo.Component));
5242   // remove dangling methods
5243   Result:=RemoveDanglingEvents(AnUnitInfo.Component, AnUnitInfo.Source, True,
5244                                ComponentModified);
5245   // update ObjectInspector1
5246   if ComponentModified
5247   and (ObjectInspector1<>nil)
5248   and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then
5249   begin
5250     ObjectInspector1.EventGrid.RefreshPropertyValues;
5251     ObjectInspector1.FavoriteGrid.RefreshPropertyValues;
5252   end;
5253 end;
5254 
5255 function RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string;
5256   var LFMCode, LRSCode: TCodeBuffer): TModalResult;
5257 var
5258   NewLFMFilename: String;
5259   OldSourceCode: String;
5260   NewSource: TCodeBuffer;
5261   NewFilePath: String;
5262   NewLRSFilePath: String;
5263   OldFilePath: String;
5264   OldLRSFilePath: String;
5265   OldFilename: String;
5266   NewLRSFilename: String;
5267   NewHighlighter: TLazSyntaxHighlighter;
5268   AmbiguousFiles: TStringList;
5269   AmbiguousText: string;
5270   i: Integer;
5271   AmbiguousFilename: String;
5272   OldUnitPath: String;
5273   OldLFMFilename: String;
5274   OldLRSFilename: String;
5275   OldPPUFilename: String;
5276   OutDir: string;
5277   Owners: TFPList;
5278   OldFileExisted: Boolean;
5279   ConvTool: TConvDelphiCodeTool;
5280 begin
5281   // Project is marked as changed already here. ToDo: Mark changed only if really renamed.
5282   Project1.BeginUpdate(true);
5283   try
5284     OldFilename:=AnUnitInfo.Filename;
5285     OldFilePath:=ExtractFilePath(OldFilename);
5286     OldLFMFilename:='';
5287     // ToDo: use UnitResources
5288     if FilenameIsPascalUnit(OldFilename) then begin
5289       OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm');
5290       if not FileExistsUTF8(OldLFMFilename) then
5291         OldLFMFilename:=ChangeFileExt(OldFilename,'.dfm');
5292     end;
5293     if NewUnitName='' then
5294       NewUnitName:=AnUnitInfo.Unit_Name;
5295     debugln(['RenameUnit ',AnUnitInfo.Filename,' NewUnitName=',NewUnitName,' OldUnitName=',AnUnitInfo.Unit_Name,' LFMCode=',LFMCode<>nil,' LRSCode=',LRSCode<>nil,' NewFilename="',NewFilename,'"']);
5296 
5297     // check new resource file
5298     NewLFMFilename:='';
5299     if FilenameIsPascalUnit(NewFilename) then
5300        NewLFMFilename:=ChangeFileExt(NewFilename,'.lfm');
5301     if AnUnitInfo.ComponentName='' then begin
5302       // unit has no component
5303       // -> remove lfm file, so that it will not be auto loaded on next open
5304       if (FileExistsUTF8(NewLFMFilename))
5305       and (not DeleteFileUTF8(NewLFMFilename))
5306       and (IDEMessageDialog(lisPkgMangDeleteFailed,
5307             Format(lisDeletingOfFileFailed, [NewLFMFilename]),
5308             mtError, [mbIgnore, mbCancel])=mrCancel)
5309       then
5310         exit(mrCancel);
5311     end;
5312 
5313     // create new source with the new filename
5314     OldSourceCode:=AnUnitInfo.Source.Source;
5315     NewSource:=CodeToolBoss.CreateFile(NewFilename);
5316     if NewSource=nil then begin
5317       Result:=IDEMessageDialog(lisUnableToCreateFile,
5318         Format(lisCanNotCreateFile, [NewFilename]),
5319         mtError,[mbCancel,mbAbort]);
5320       exit;
5321     end;
5322     NewSource.Source:=OldSourceCode;
5323     if (AnUnitInfo.Source.DiskEncoding<>'') and (AnUnitInfo.Source.DiskEncoding<>EncodingUTF8)
5324     then begin
5325       NewSource.DiskEncoding:=AnUnitInfo.Source.DiskEncoding;
5326       InputHistories.FileEncodings[NewFilename]:=NewSource.DiskEncoding;
5327     end else
5328       InputHistories.FileEncodings.Remove(NewFilename);
5329 
5330     // get final filename
5331     NewFilename:=NewSource.Filename;
5332     NewFilePath:=ExtractFilePath(NewFilename);
5333     EnvironmentOptions.RemoveFromRecentOpenFiles(OldFilename);
5334     EnvironmentOptions.AddToRecentOpenFiles(NewFilename);
5335     MainIDE.SetRecentFilesMenu;
5336 
5337     // add new path to unit path
5338     if AnUnitInfo.IsPartOfProject
5339     and (FilenameIsPascalUnit(NewFilename))
5340     and (CompareFilenames(NewFilePath,Project1.Directory)<>0) then begin
5341       OldUnitPath:=Project1.CompilerOptions.GetUnitPath(false);
5342       if SearchDirectoryInSearchPath(OldUnitPath,NewFilePath,1)<1 then
5343         AddPathToBuildModes(NewFilePath, False);
5344     end;
5345 
5346     // rename lfm file
5347     if FilenameIsAbsolute(NewLFMFilename) then begin
5348       if (LFMCode=nil)
5349       and (OldLFMFilename<>'')
5350       and FilenameIsAbsolute(OldLFMFilename) and FileExistsUTF8(OldLFMFilename) then
5351         LFMCode:=CodeToolBoss.LoadFile(OldLFMFilename,false,false);
5352       if (LFMCode<>nil) then begin
5353         Result:=SaveCodeBufferToFile(LFMCode,NewLFMFilename,true);
5354         if not (Result in [mrOk,mrIgnore]) then begin
5355           DebugLn(['RenameUnit SaveCodeBufferToFile failed for "',NewLFMFilename,'"']);
5356           exit;
5357         end;
5358         LFMCode:=CodeToolBoss.LoadFile(NewLFMFilename,true,false);
5359         if LFMCode<>nil then
5360           NewLFMFilename:=LFMCode.Filename;
5361         ConvTool:=TConvDelphiCodeTool.Create(NewSource);
5362         try
5363           if not ConvTool.RenameResourceDirectives then
5364             debugln(['RenameUnit WARNING: unable to rename resource directive in "',NewSource.Filename,'"']);
5365         finally
5366           ConvTool.Free;
5367         end;
5368       end;
5369     end;
5370 
5371     // rename Resource file (.lrs)
5372     if (LRSCode<>nil) then begin
5373       // the resource include line in the code will be changed later after
5374       // changing the unitname
5375       if AnUnitInfo.IsPartOfProject
5376       and (not Project1.IsVirtual)
5377       and (pfLRSFilesInOutputDirectory in Project1.Flags) then begin
5378         NewLRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo);
5379         NewLRSFilename:=AppendPathDelim(ExtractFilePath(NewLRSFilename))
5380           +ExtractFileNameOnly(NewFilename)+ResourceFileExt;
5381       end else begin
5382         OldLRSFilePath:=ExtractFilePath(LRSCode.Filename);
5383         NewLRSFilePath:=OldLRSFilePath;
5384         if FilenameIsAbsolute(OldFilePath)
5385         and PathIsInPath(OldLRSFilePath,OldFilePath) then begin
5386           // resource code was in the same or in a sub directory of source
5387           // -> try to keep this relationship
5388           NewLRSFilePath:=NewFilePath
5389             +copy(LRSCode.Filename,length(OldFilePath)+1,length(LRSCode.Filename));
5390           if not DirPathExists(NewLRSFilePath) then
5391             NewLRSFilePath:=NewFilePath;
5392         end else begin
5393           // resource code was not in the same or in a sub directory of source
5394           // copy resource into the same directory as the source
5395           NewLRSFilePath:=NewFilePath;
5396         end;
5397         NewLRSFilename:=NewLRSFilePath+ExtractFileNameOnly(NewFilename)+ResourceFileExt;
5398       end;
5399       Result:=ForceDirectoryInteractive(ExtractFilePath(NewLRSFilename),[mbRetry,mbIgnore]);
5400       if Result=mrCancel then exit;
5401       if Result=mrOk then begin
5402         if not CodeToolBoss.SaveBufferAs(LRSCode,NewLRSFilename,LRSCode) then
5403           DebugLn(['RenameUnit CodeToolBoss.SaveBufferAs failed: NewResFilename="',NewLRSFilename,'"']);
5404       end;
5405 
5406       {$IFDEF IDE_DEBUG}
5407       debugln(['RenameUnit C ',ResourceCode<>nil]);
5408       debugln(['   NewResFilePath="',NewResFilePath,'" NewResFilename="',NewResFilename,'"']);
5409       if ResourceCode<>nil then debugln('*** ResourceFileName ',ResourceCode.Filename);
5410       {$ENDIF}
5411     end else begin
5412       NewLRSFilename:='';
5413     end;
5414     // rename unit name of jit class
5415     if (AnUnitInfo.Component<>nil) then
5416       FormEditor1.RenameJITComponentUnitname(AnUnitInfo.Component,NewUnitName);
5417     {$IFDEF IDE_DEBUG}
5418     if AnUnitInfo.Component<>nil then debugln('*** AnUnitInfo.Component ',dbgsName(AnUnitInfo.Component),' ClassUnitname=',GetClassUnitName(AnUnitInfo.Component.ClassType));
5419     debugln(['RenameUnit D ',ResourceCode<>nil]);
5420     {$ENDIF}
5421 
5422     // set new codebuffer in unitinfo and sourceeditor
5423     AnUnitInfo.Source:=NewSource;
5424     if AnUnitInfo.IsPartOfProject then
5425       Project1.Modified:=true
5426     else
5427       Project1.SessionModified:=true;
5428      AnUnitInfo.ClearModifieds;
5429     for i := 0 to AnUnitInfo.EditorInfoCount -1 do
5430       if AnUnitInfo.EditorInfo[i].EditorComponent <> nil then
5431         TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).CodeBuffer := NewSource;
5432         // the code is not changed, therefore the marks are kept
5433 
5434     // change unitname in lpi and in main source file
5435     AnUnitInfo.Unit_Name:=NewUnitName;
5436     if LRSCode<>nil then begin
5437       // change resource filename in the source include directive
5438       if not CodeToolBoss.RenameMainInclude(AnUnitInfo.Source,
5439         ExtractFilename(LRSCode.Filename),false)
5440       then
5441         DebugLn(['RenameUnit CodeToolBoss.RenameMainInclude failed: AnUnitInfo.Source="',AnUnitInfo.Source,'" ResourceCode="',ExtractFilename(LRSCode.Filename),'"']);
5442     end;
5443 
5444     // change unitname on SourceNotebook
5445     if AnUnitInfo.OpenEditorInfoCount > 0 then
5446       UpdateSourceNames;
5447 
5448     // change syntax highlighter
5449     NewHighlighter:=FilenameToLazSyntaxHighlighter(NewFilename);
5450     AnUnitInfo.UpdateDefaultHighlighter(NewHighlighter);
5451     for i := 0 to AnUnitInfo.EditorInfoCount - 1 do
5452       if (AnUnitInfo.EditorInfo[i].EditorComponent <> nil) and
5453          (not AnUnitInfo.EditorInfo[i].CustomHighlighter)
5454       then
5455         TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).SyntaxHighlighterType :=
5456           AnUnitInfo.EditorInfo[i].SyntaxHighlighter;
5457 
5458     // save file
5459     if not NewSource.IsVirtual then begin
5460       // notify packages
5461       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,sefsBeforeWrite);
5462       if Result<>mrOk then exit;
5463       // actual write
5464       Result:=AnUnitInfo.WriteUnitSource;
5465       if Result<>mrOk then exit;
5466       AnUnitInfo.Modified:=false;
5467       // notify packages
5468       Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,sefsAfterWrite);
5469       if Result<>mrOk then exit;
5470     end;
5471 
5472     // change lpks containing the file
5473     Result:=PkgBoss.OnRenameFile(OldFilename,AnUnitInfo.Filename,
5474                                  AnUnitInfo.IsPartOfProject);
5475     if Result=mrAbort then exit;
5476 
5477     OldFileExisted:=FilenameIsAbsolute(OldFilename) and FileExistsUTF8(OldFilename);
5478 
5479     // delete ambiguous files
5480     NewFilePath:=ExtractFilePath(NewFilename);
5481     AmbiguousFiles:=
5482       FindFilesCaseInsensitive(NewFilePath,ExtractFilename(NewFilename),true);
5483     if AmbiguousFiles<>nil then begin
5484       try
5485         if (AmbiguousFiles.Count=1)
5486         and (CompareFilenames(OldFilePath,NewFilePath)=0)
5487         and (CompareFilenames(AmbiguousFiles[0],ExtractFilename(OldFilename))=0)
5488         then
5489           AmbiguousText:=Format(lisDeleteOldFile, [ExtractFilename(OldFilename)])
5490         else
5491           AmbiguousText:=Format(lisThereAreOtherFilesInTheDirectoryWithTheSameName,
5492                           [LineEnding, LineEnding, AmbiguousFiles.Text, LineEnding]);
5493         Result:=IDEMessageDialog(lisAmbiguousFilesFound, AmbiguousText,
5494           mtWarning,[mbYes,mbNo,mbAbort]);
5495         if Result=mrAbort then exit;
5496         if Result=mrYes then begin
5497           NewFilePath:=AppendPathDelim(ExtractFilePath(NewFilename));
5498           for i:=0 to AmbiguousFiles.Count-1 do begin
5499             AmbiguousFilename:=NewFilePath+AmbiguousFiles[i];
5500             if (FileExistsUTF8(AmbiguousFilename))
5501             and (not DeleteFileUTF8(AmbiguousFilename))
5502             and (IDEMessageDialog(lisPkgMangDeleteFailed,
5503                   Format(lisDeletingOfFileFailed, [AmbiguousFilename]),
5504                   mtError, [mbIgnore, mbCancel])=mrCancel)
5505             then
5506               exit(mrCancel);
5507           end;
5508         end;
5509       finally
5510         AmbiguousFiles.Free;
5511       end;
5512     end;
5513 
5514     // remove old path from unit path
5515     if AnUnitInfo.IsPartOfProject
5516     and (FilenameIsPascalUnit(OldFilename))
5517     and (OldFilePath<>'') then begin
5518       //DebugLn('RenameUnit OldFilePath="',OldFilePath,'" SourceDirs="',Project1.SourceDirectories.CreateSearchPathFromAllFiles,'"');
5519       if (SearchDirectoryInSearchPath(
5520         Project1.SourceDirectories.CreateSearchPathFromAllFiles,OldFilePath,1)<1)
5521       then
5522         //DebugLn('RenameUnit OldFilePath="',OldFilePath,'" UnitPath="',Project1.CompilerOptions.GetUnitPath(false),'"');
5523         if (SearchDirectoryInSearchPath(Project1.CompilerOptions.GetUnitPath(false),OldFilePath,1)<1)
5524         then
5525           if IDEMessageDialog(lisCleanUpUnitPath,
5526               Format(lisTheDirectoryIsNoLongerNeededInTheUnitPathRemoveIt,[OldFilePath,LineEnding]),
5527               mtConfirmation,[mbYes,mbNo])=mrYes
5528           then
5529             Project1.CompilerOptions.RemoveFromUnitPaths(OldUnitPath);
5530     end;
5531 
5532     // delete old pas, .pp, .ppu
5533     if (CompareFilenames(NewFilename,OldFilename)<>0)
5534     and OldFileExisted then begin
5535       if IDEMessageDialog(lisDeleteOldFile2, Format(lisDeleteOldFile,[OldFilename]),
5536         mtConfirmation,[mbYes,mbNo])=mrYes then
5537       begin
5538         Result:=DeleteFileInteractive(OldFilename,[mbAbort]);
5539         if Result=mrAbort then exit;
5540         // delete old lfm
5541         //debugln(['RenameUnit NewLFMFilename=',NewLFMFilename,' exists=',FileExistsUTF8(NewLFMFilename),' Old=',OldLFMFilename,' exists=',FileExistsUTF8(OldLFMFilename)]);
5542         if FileExistsUTF8(NewLFMFilename) then begin
5543           // the new file has a lfm, so it is safe to delete the old
5544           // (if NewLFMFilename does not exist, it didn't belong to the unit
5545           //  or there was an error during delete. Never delete files in doubt.)
5546           OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm');
5547           if FileExistsUTF8(OldLFMFilename) then begin
5548             Result:=DeleteFileInteractive(OldLFMFilename,[mbAbort]);
5549             if Result=mrAbort then exit;
5550           end;
5551         end;
5552         // delete old lrs
5553         if (LRSCode<>nil) and FileExistsUTF8(LRSCode.Filename) then begin
5554           // the new file has a lrs, so it is safe to delete the old
5555           // (if the new lrs does not exist, it didn't belong to the unit
5556           //  or there was an error during delete. Never delete files in doubt.)
5557           OldLRSFilename:=ChangeFileExt(OldFilename,ResourceFileExt);
5558           if FileExistsUTF8(OldLRSFilename) then begin
5559             Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]);
5560             if Result=mrAbort then exit;
5561           end;
5562         end;
5563         // delete ppu in source directory
5564         OldPPUFilename:=ChangeFileExt(OldFilename,'.ppu');
5565         if FileExistsUTF8(OldPPUFilename) then begin
5566           Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5567           if Result=mrAbort then exit;
5568         end;
5569         OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o');
5570         if FileExistsUTF8(OldPPUFilename) then begin
5571           Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5572           if Result=mrAbort then exit;
5573         end;
5574         Owners:=PkgBoss.GetOwnersOfUnit(NewFilename);
5575         try
5576           if Owners<>nil then begin
5577             for i:=0 to Owners.Count-1 do begin
5578               OutDir:='';
5579               if TObject(Owners[i]) is TProject then begin
5580                 // delete old files in project output directory
5581                 OutDir:=TProject(Owners[i]).CompilerOptions.GetUnitOutPath(false);
5582               end else if TObject(Owners[i]) is TLazPackage then begin
5583                 // delete old files in package output directory
5584                 OutDir:=TLazPackage(Owners[i]).CompilerOptions.GetUnitOutPath(false);
5585               end;
5586               if (OutDir<>'') and FilenameIsAbsolute(OutDir) then begin
5587                 OldPPUFilename:=AppendPathDelim(OutDir)+ChangeFileExt(ExtractFilenameOnly(OldFilename),'.ppu');
5588                 if FileExistsUTF8(OldPPUFilename) then begin
5589                   Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5590                   if Result=mrAbort then exit;
5591                 end;
5592                 OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o');
5593                 if FileExistsUTF8(OldPPUFilename) then begin
5594                   Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5595                   if Result=mrAbort then exit;
5596                 end;
5597                 OldLRSFilename:=ChangeFileExt(OldPPUFilename,ResourceFileExt);
5598                 if FileExistsUTF8(OldLRSFilename) then begin
5599                   Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]);
5600                   if Result=mrAbort then exit;
5601                 end;
5602               end;
5603             end;
5604           end;
5605         finally
5606           Owners.Free;
5607         end;
5608       end;
5609     end;
5610 
5611   finally
5612     Project1.EndUpdate;
5613   end;
5614   Result:=mrOk;
5615 end;
5616 
5617 function RenameUnitLowerCase(AnUnitInfo: TUnitInfo; AskUser: boolean): TModalresult;
5618 var
5619   OldFilename: String;
5620   OldShortFilename: String;
5621   NewFilename: String;
5622   NewShortFilename: String;
5623   LFMCode, LRSCode: TCodeBuffer;
5624   NewUnitName: String;
5625 begin
5626   Result:=mrOk;
5627   OldFilename:=AnUnitInfo.Filename;
5628   // check if file is unit
5629   if not FilenameIsPascalUnit(OldFilename) then exit;
5630   // check if file is already lowercase (or it does not matter in current OS)
5631   OldShortFilename:=ExtractFilename(OldFilename);
5632   NewShortFilename:=lowercase(OldShortFilename);
5633   if CompareFilenames(OldShortFilename,NewShortFilename)=0 then exit;
5634   // create new filename
5635   NewFilename:=ExtractFilePath(OldFilename)+NewShortFilename;
5636 
5637   // rename unit
5638   if AskUser then begin
5639     Result:=IDEQuestionDialog(lisFileNotLowercase,
5640       Format(lisTheUnitIsNotLowercaseTheFreePascalCompiler,
5641              [OldFilename, LineEnding, LineEnding+LineEnding]),
5642       mtConfirmation,[mrYes,mrIgnore,rsmbNo,mrAbort],'');
5643     if Result<>mrYes then exit;
5644   end;
5645   NewUnitName:=AnUnitInfo.Unit_Name;
5646   if NewUnitName='' then begin
5647     AnUnitInfo.ReadUnitNameFromSource(false);
5648     NewUnitName:=AnUnitInfo.CreateUnitName;
5649   end;
5650   LFMCode:=nil;
5651   LRSCode:=nil;
5652   Result:=RenameUnit(AnUnitInfo,NewFilename,NewUnitName,LFMCode,LRSCode);
5653 end;
5654 
5655 function CheckLFMInEditor(LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult;
5656 var
5657   LFMChecker: TLFMChecker;
5658   UnitFilename: String;
5659   PascalBuf: TCodeBuffer;
5660   i: integer;
5661   LFMFilename: String;
5662   SrcEdit: TSourceEditor;
5663 begin
5664   if (LFMUnitInfo<>nil)
5665   and FilenameIsPascalUnit(LFMUnitInfo.Filename) then begin
5666     LFMFilename:=ChangeFileExt(LFMUnitInfo.Filename,'.lfm');
5667     if FileExistsInIDE(LFMFilename,[])
5668     and (OpenEditorFile(LFMFilename,-1,-1,nil,[])=mrOk)
5669     and (SourceEditorManager.ActiveEditor<>nil)
5670     then begin
5671       SrcEdit:=SourceEditorManager.ActiveEditor;
5672       LFMUnitInfo:=Project1.UnitInfoWithFilename(SrcEdit.FileName);
5673     end;
5674   end;
5675 
5676   // check, if a .lfm file is opened in the source editor
5677   if (LFMUnitInfo=nil) or
5678     ((CompareFileExt(LFMUnitInfo.Filename,'.lfm',false)<>0) and
5679      (CompareFileExt(LFMUnitInfo.Filename,'.dfm',false)<>0)) then
5680   begin
5681     if not Quiet then
5682     begin
5683       IDEMessageDialog(lisNoLFMFile,
5684         lisThisFunctionNeedsAnOpenLfmFileInTheSourceEditor,
5685         mtError,[mbCancel]);
5686     end;
5687     Result:=mrCancel;
5688     exit;
5689   end;
5690   // try to find the pascal unit
5691   for i:=Low(PascalFileExt) to High(PascalFileExt) do begin
5692     UnitFilename:=ChangeFileExt(LFMUnitInfo.Filename,PascalFileExt[i]);
5693     if FileExistsCached(UnitFilename) then
5694       break
5695     else
5696       UnitFilename:='';
5697   end;
5698   if UnitFilename='' then begin
5699     IDEMessageDialog(lisNoPascalFile,
5700       Format(lisUnableToFindPascalUnitPasPpForLfmFile,[LineEnding, LFMUnitInfo.Filename]),
5701       mtError,[mbCancel]);
5702     Result:=mrCancel;
5703     exit;
5704   end;
5705 
5706   if MainIDE.ToolStatus<>itNone then begin
5707     DebugLn(['CheckLFMInEditor ToolStatus<>itNone']);
5708     Result:=mrCancel;
5709     exit;
5710   end;
5711   // load the pascal unit
5712   SaveEditorChangesToCodeCache(nil);
5713   Result:=LoadCodeBuffer(PascalBuf,UnitFilename,[],false);
5714   if Result<>mrOk then exit;
5715 
5716   // open messages window
5717   SourceEditorManager.ClearErrorLines;
5718   if MessagesView<>nil then
5719     MessagesView.Clear;
5720   ArrangeSourceEditorAndMessageView(false);
5721 
5722   // parse the LFM file and the pascal unit
5723   LFMChecker:=TLFMChecker.Create(PascalBuf,LFMUnitInfo.Source);
5724   try
5725     LFMChecker.ShowMessages:=true;
5726     LFMChecker.RootMustBeClassInUnit:=true;
5727     LFMChecker.RootMustBeClassInIntf:=true;
5728     LFMChecker.ObjectsMustExist:=true;
5729     if LFMChecker.Repair=mrOk then begin
5730       if not Quiet then begin
5731         IDEMessageDialog(lisLFMIsOk,
5732           lisClassesAndPropertiesExistValuesWereNotChecked,
5733           mtInformation,[mbOk],'');
5734       end;
5735     end else begin
5736       MainIDE.DoJumpToCompilerMessage(true);
5737       Result:=mrAbort;
5738       exit;
5739     end;
5740   finally
5741     LFMChecker.Free;
5742   end;
5743   Result:=mrOk;
5744 end;
5745 
5746 function LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer;
5747   AutoCreateResourceCode, ShowAbort: boolean): TModalResult;
5748 var
5749   LFMFilename: string;
5750   LRSFilename: String;
5751   ResType: TResourceType;
5752 begin
5753   LFMCode:=nil;
5754   LRSCode:=nil;
5755   //DebugLn(['LoadResourceFile ',AnUnitInfo.Filename,' HasResources=',AnUnitInfo.HasResources,' IgnoreSourceErrors=',IgnoreSourceErrors,' AutoCreateResourceCode=',AutoCreateResourceCode]);
5756   // Load the lfm file (without parsing)
5757   if not AnUnitInfo.IsVirtual then begin  // and (AnUnitInfo.Component<>nil)
5758     LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,true);
5759     if (FileExistsCached(LFMFilename)) then begin
5760       Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],ShowAbort);
5761       if not (Result in [mrOk,mrIgnore]) then
5762         exit;
5763     end;
5764   end;
5765   if AnUnitInfo.HasResources then begin
5766     //debugln('LoadResourceFile A "',AnUnitInfo.Filename,'" "',AnUnitInfo.ResourceFileName,'"');
5767     ResType:=MainBuildBoss.GetResourceType(AnUnitInfo);
5768     if ResType=rtLRS then begin
5769       LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,false);
5770       if LRSFilename<>'' then begin
5771         Result:=LoadCodeBuffer(LRSCode,LRSFilename,[lbfUpdateFromDisk],ShowAbort);
5772         if Result<>mrOk then exit;
5773       end else begin
5774         LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo);
5775         if AutoCreateResourceCode then begin
5776           LRSCode:=CodeToolBoss.CreateFile(LRSFilename);
5777         end else begin
5778           DebugLn(['LoadResourceFile .lrs file not found of unit ',AnUnitInfo.Filename]);
5779           exit(mrCancel);
5780         end;
5781       end;
5782     end else begin
5783       LRSFilename:='';
5784       LRSCode:=nil;
5785     end;
5786   end;
5787   Result:=mrOk;
5788 end;
5789 
5790 function LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags;
5791   CloseFlags: TCloseFlags): TModalResult;
5792 // if there is a .lfm file, open the resource
5793 var
5794   UnitResourceFilename: string;
5795   UnitResourceFileformat: TUnitResourcefileFormatClass;
5796   LFMBuf: TCodeBuffer;
5797   CanAbort: boolean;
5798 begin
5799   CanAbort:=[ofProjectLoading,ofMultiOpen]*OpenFlags<>[];
5800 
5801   UnitResourceFileformat:=AnUnitInfo.UnitResourceFileformat;
5802   // Note: think about virtual and normal .lfm files.
5803   UnitResourceFilename:=UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,true);
5804   LFMBuf:=nil;
5805   if not FileExistsInIDE(UnitResourceFilename,[pfsfOnlyEditorFiles]) then begin
5806     // there is no LFM file -> ok
5807     {$IFDEF IDE_DEBUG}
5808     debugln('LoadLFM there is no LFM file for "',AnUnitInfo.Filename,'"');
5809     {$ENDIF}
5810     Result:=mrOk;
5811     exit;
5812   end;
5813 
5814   // there is a lazarus form text file -> load it
5815   Result:=LoadIDECodeBuffer(LFMBuf,UnitResourceFilename,[lbfUpdateFromDisk],CanAbort);
5816   if Result<>mrOk then begin
5817     DebugLn(['LoadLFM LoadIDECodeBuffer failed']);
5818     exit;
5819   end;
5820   Result:=LoadLFM(AnUnitInfo,LFMBuf,OpenFlags,CloseFlags);
5821 end;
5822 
5823 function LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer;
5824   OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult;
5825 const
5826   BufSize = 4096; // allocating mem in 4k chunks helps many mem managers
5827 
5828   ShowCommands: array[TWindowState] of Integer =
5829     (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
5830 
5831 var
5832   TxtLFMStream, BinStream: TExtMemoryStream;
5833   NewComponent: TComponent;
5834   AncestorType: TComponentClass;
5835   DesignerForm: TCustomForm;
5836   NewClassName: String;
5837   LFMType: String;
5838   ACaption, AText: String;
5839   NewUnitName: String;
5840   AncestorUnitInfo, NestedUnitInfo, LFMUnitInfo: TUnitInfo;
5841   ReferencesLocked: Boolean;
5842   LCLVersion: string;
5843   MissingClasses: TStrings;
5844   LFMComponentName: string;
5845   i: Integer;
5846   NestedClassName: string;
5847   NestedClass: TComponentClass;
5848   DisableAutoSize: Boolean;
5849   PreventAutoSize: Boolean;
5850   NewControl: TControl;
5851   ARestoreVisible: Boolean;
5852   AncestorClass: TComponentClass;
5853   DsgControl: TCustomDesignControl;
5854   {$IF (FPC_FULLVERSION >= 30003)}
5855   DsgDataModule: TDataModule;
5856   {$ENDIF}
5857 begin
5858   {$IFDEF IDE_DEBUG}
5859   debugln('LoadLFM A ',AnUnitInfo.Filename,' IsPartOfProject=',dbgs(AnUnitInfo.IsPartOfProject),' ');
5860   {$ENDIF}
5861 
5862   ReferencesLocked:=false;
5863   MissingClasses:=nil;
5864   NewComponent:=nil;
5865   try
5866     if (ofRevert in OpenFlags) and (AnUnitInfo.Component<>nil) then begin
5867       // the component must be destroyed and recreated => store references
5868       ReferencesLocked:=true;
5869       Project1.LockUnitComponentDependencies;
5870       Project1.UpdateUnitComponentDependencies;
5871 
5872       // close old designer form
5873       Result:=CloseUnitComponent(AnUnitInfo,CloseFlags);
5874       if Result<>mrOk then begin
5875         DebugLn(['LoadLFM CloseUnitComponent failed']);
5876         exit;
5877       end;
5878     end;
5879 
5880     // check installed packages
5881     if EnvironmentOptions.CheckPackagesOnFormCreate and
5882        (AnUnitInfo.Component = nil) and
5883         AnUnitInfo.IsPartOfProject and
5884        (not (ofProjectLoading in OpenFlags)) then
5885     begin
5886       // opening a form of the project -> check installed packages
5887       Result := PkgBoss.CheckProjectHasInstalledPackages(Project1,
5888                                        OpenFlags * [ofProjectLoading, ofQuiet] = []);
5889       if not (Result in [mrOk, mrIgnore]) then
5890       begin
5891         DebugLn(['LoadLFM PkgBoss.CheckProjectHasInstalledPackages failed']);
5892         exit;
5893       end;
5894     end;
5895     {$IFDEF VerboseLFMSearch}
5896     debugln('LoadLFM LFM file loaded, parsing "',LFMBuf.Filename,'" ...');
5897     {$ENDIF}
5898 
5899     // someone created a .lfm file -> Update HasResources
5900     AnUnitInfo.HasResources:=true;
5901 
5902     // find the classname of the LFM, and check for inherited form
5903     AnUnitInfo.UnitResourceFileformat.QuickCheckResourceBuffer(
5904       AnUnitInfo.Source,LFMBuf,LFMType,LFMComponentName,
5905       NewClassName,LCLVersion,MissingClasses);
5906 
5907     {$IFDEF VerboseLFMSearch}
5908     debugln('LoadLFM LFM="',LFMBuf.Source,'"');
5909     {$ENDIF}
5910     if AnUnitInfo.Component=nil then begin
5911       // load/create new instance
5912 
5913       if (NewClassName='') or (LFMType='') then begin
5914         DebugLn(['LoadLFM LFM file corrupt']);
5915         Result:=IDEMessageDialog(lisLFMFileCorrupt,
5916           Format(lisUnableToFindAValidClassnameIn, [LFMBuf.Filename]),
5917           mtError,[mbIgnore,mbCancel,mbAbort]);
5918         exit;
5919       end;
5920 
5921       // load missing component classes (e.g. ancestor and frames)
5922       Result:=LoadAncestorDependencyHidden(AnUnitInfo,NewClassName,OpenFlags,
5923                                            AncestorType,AncestorUnitInfo);
5924       if Result<>mrOk then begin
5925         DebugLn(['LoadLFM DoLoadAncestorDependencyHidden failed for ',AnUnitInfo.Filename]);
5926         exit;
5927       end;
5928 
5929       if MissingClasses<>nil then begin
5930         {$IFDEF VerboseLFMSearch}
5931         DebugLn(['LoadLFM has nested: ',AnUnitInfo.Filename]);
5932         {$ENDIF}
5933         for i:=MissingClasses.Count-1 downto 0 do begin
5934           NestedClassName:=MissingClasses[i];
5935           {$IFDEF VerboseLFMSearch}
5936           DebugLn(['LoadLFM nested ',i,' ',MissingClasses.Count,': ',NestedClassName]);
5937           {$ENDIF}
5938           if SysUtils.CompareText(NestedClassName,AncestorType.ClassName)=0 then
5939           begin
5940             MissingClasses.Delete(i);
5941           end else begin
5942             DebugLn(['LoadLFM loading nested class ',NestedClassName,' needed by ',AnUnitInfo.Filename]);
5943             NestedClass:=nil;
5944             NestedUnitInfo:=nil;
5945             Result:=LoadComponentDependencyHidden(AnUnitInfo,NestedClassName,
5946                       OpenFlags,
5947                       {$IFDEF EnableNestedComponentsWithoutLFM}
5948                       false,
5949                       {$ELSE}
5950                       true,
5951                       {$ENDIF}
5952                       NestedClass,NestedUnitInfo,AncestorClass);
5953             if Result<>mrOk then begin
5954               DebugLn(['LoadLFM DoLoadComponentDependencyHidden NestedClassName=',NestedClassName,' failed for ',AnUnitInfo.Filename]);
5955               exit;
5956             end;
5957             if NestedClass<>nil then
5958               MissingClasses.Objects[i]:=TObject(Pointer(NestedClass))
5959             else if AncestorClass<>nil then
5960               MissingClasses.Objects[i]:=TObject(Pointer(AncestorClass));
5961           end;
5962         end;
5963         //DebugLn(['LoadLFM had nested: ',AnUnitInfo.Filename]);
5964         if AnUnitInfo.ComponentFallbackClasses<>nil then begin
5965           AnUnitInfo.ComponentFallbackClasses.Free;
5966           AnUnitInfo.ComponentFallbackClasses:=nil;
5967         end;
5968         AnUnitInfo.ComponentFallbackClasses:=MissingClasses;
5969         MissingClasses:=nil;
5970       end;
5971 
5972       BinStream:=nil;
5973       try
5974         // convert text to binary format
5975         BinStream:=TExtMemoryStream.Create;
5976         TxtLFMStream:=TExtMemoryStream.Create;
5977         try
5978           {$IFDEF VerboseIDELFMConversion}
5979           DebugLn(['LoadLFM LFMBuf START =======================================']);
5980           DebugLn(LFMBuf.Source);
5981           DebugLn(['LoadLFM LFMBuf END   =======================================']);
5982           {$ENDIF}
5983           LFMBuf.SaveToStream(TxtLFMStream);
5984           AnUnitInfo.ComponentLastLFMStreamSize:=TxtLFMStream.Size;
5985           TxtLFMStream.Position:=0;
5986 
5987           try
5988             if AnUnitInfo.ComponentLastBinStreamSize>0 then
5989               BinStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+BufSize;
5990             AnUnitInfo.UnitResourceFileformat.TextStreamToBinStream(TxtLFMStream, BinStream);
5991             AnUnitInfo.ComponentLastBinStreamSize:=BinStream.Size;
5992             BinStream.Position:=0;
5993 
5994             {$IFDEF VerboseIDELFMConversion}
5995             DebugLn(['LoadLFM Binary START =======================================']);
5996             debugln(dbgMemStream(BinStream,BinStream.Size));
5997             DebugLn(['LoadLFM Binary END   =======================================']);
5998             BinStream.Position:=0;
5999             {$ENDIF}
6000 
6001             Result:=mrOk;
6002           except
6003             on E: Exception do begin
6004               DumpExceptionBackTrace;
6005               ACaption:=lisFormatError;
6006               AText:=Format(lisUnableToConvertTextFormDataOfFileIntoBinaryStream,
6007                 [LineEnding, LFMBuf.Filename, LineEnding, E.Message]);
6008               Result:=IDEMessageDialog(ACaption, AText, mtError, [mbOk, mbCancel]);
6009               if Result=mrCancel then Result:=mrAbort;
6010               exit;
6011             end;
6012           end;
6013         finally
6014           TxtLFMStream.Free;
6015         end;
6016         if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then
6017           FormEditor1.ClearSelection;
6018 
6019         // create JIT component
6020         NewUnitName:=AnUnitInfo.Unit_Name;
6021         if NewUnitName='' then
6022           NewUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename);
6023         DisableAutoSize:=true;
6024         NewComponent:=FormEditor1.CreateRawComponentFromStream(BinStream,
6025           AnUnitInfo.UnitResourceFileformat,
6026           AncestorType,copy(NewUnitName,1,255),true,true,DisableAutoSize,AnUnitInfo);
6027         if (NewComponent is TControl) then begin
6028           NewControl:=TControl(NewComponent);
6029           if ofLoadHiddenResource in OpenFlags then
6030             NewControl.ControlStyle:=NewControl.ControlStyle+[csNoDesignVisible];
6031           if DisableAutoSize then
6032           begin
6033             PreventAutoSize := (IDETabMaster <> nil)
6034                                and (NewControl is TCustomDesignControl)
6035                                and IDETabMaster.AutoSizeInShowDesigner(NewControl);
6036             if not PreventAutoSize then
6037               NewControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster Delayed'){$ENDIF};
6038           end;
6039         end;
6040 
6041         if NewComponent is TCustomDesignControl then
6042         begin
6043           DsgControl := TCustomDesignControl(NewComponent);
6044           if (Project1.Scaled or EnvironmentOptions.ForceDPIScalingInDesignTime)
6045           and DsgControl.Scaled and (DsgControl.DesignTimePPI<>Screen.PixelsPerInch) then
6046           begin
6047             DsgControl.AutoAdjustLayout(lapAutoAdjustForDPI, DsgControl.DesignTimePPI, Screen.PixelsPerInch, 0, 0);
6048             DesignerProcs.ScaleNonVisual(DsgControl, DsgControl.DesignTimePPI, Screen.PixelsPerInch);
6049           end;
6050           DsgControl.DesignTimePPI := Screen.PixelsPerInch;
6051           DsgControl.PixelsPerInch := Screen.PixelsPerInch;
6052         end;
6053         {$IF (FPC_FULLVERSION >= 30003)} // TDataModule.DesignPPI was added in FPC 3.0.3
6054         if NewComponent is TDataModule then
6055         begin
6056           DsgDataModule := TDataModule(NewComponent);
6057           if (DsgDataModule.DesignPPI<>Screen.PixelsPerInch) then
6058           begin
6059             DesignerProcs.ScaleNonVisual(DsgDataModule, DsgDataModule.DesignPPI, Screen.PixelsPerInch);
6060             DsgDataModule.DesignOffset := Point(
6061               MulDiv(DsgDataModule.DesignOffset.x, Screen.PixelsPerInch, DsgDataModule.DesignPPI),
6062               MulDiv(DsgDataModule.DesignOffset.y, Screen.PixelsPerInch, DsgDataModule.DesignPPI));
6063             DsgDataModule.DesignSize := Point(
6064               MulDiv(DsgDataModule.DesignSize.x, Screen.PixelsPerInch, DsgDataModule.DesignPPI),
6065               MulDiv(DsgDataModule.DesignSize.y, Screen.PixelsPerInch, DsgDataModule.DesignPPI));
6066             DsgDataModule.DesignPPI := Screen.PixelsPerInch;
6067           end;
6068         end;
6069         {$ENDIF}
6070 
6071         AnUnitInfo.ResourceBaseClass:=GetComponentBaseClass(NewComponent.ClassType);
6072 
6073         Project1.InvalidateUnitComponentDesignerDependencies;
6074         AnUnitInfo.Component:=NewComponent;
6075         if (AncestorUnitInfo<>nil) then
6076           AnUnitInfo.AddRequiresComponentDependency(AncestorUnitInfo,[ucdtAncestor]);
6077         if NewComponent<>nil then begin
6078           // component loaded, now load the referenced units
6079           Result:=MainIDE.DoFixupComponentReferences(AnUnitInfo.Component,OpenFlags);
6080           if Result<>mrOk then begin
6081             DebugLn(['LoadLFM DoFixupComponentReferences failed']);
6082             exit;
6083           end;
6084         end else begin
6085           // error streaming component -> examine lfm file
6086           DebugLn('ERROR: streaming failed lfm="',LFMBuf.Filename,'"');
6087           // open lfm file in editor
6088           if AnUnitInfo.OpenEditorInfoCount > 0 then
6089             Result:=OpenEditorFile(LFMBuf.Filename,
6090               AnUnitInfo.OpenEditorInfo[0].PageIndex+1,
6091               AnUnitInfo.OpenEditorInfo[0].WindowID, Nil,
6092               OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile], True)
6093           else
6094             Result:=OpenEditorFile(LFMBuf.Filename, -1, -1, nil,
6095               OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile]);
6096           if Result<>mrOk then begin
6097             DebugLn(['LoadLFM DoOpenEditorFile failed']);
6098             exit;
6099           end;
6100           LFMUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor);
6101           Result:=CheckLFMInEditor(LFMUnitInfo, true);
6102           if Result=mrOk then Result:=mrCancel;
6103           exit;
6104         end;
6105       finally
6106         BinStream.Free;
6107       end;
6108     end else if SysUtils.CompareText(AnUnitInfo.Component.ClassName,NewClassName)<>0
6109     then begin
6110       // lfm and current designer are about different classes
6111       debugln(['LoadLFM unit="',AnUnitInfo.Filename,'": loaded component has class "',AnUnitInfo.Component.ClassName,'", lfm has class "',NewClassName,'"']);
6112       // keep old instance, add a designer, so user can see current component
6113     end else begin
6114       // make hidden component visible, keep old instance, add a designer
6115       DebugLn(['LoadLFM Creating designer for hidden component of ',AnUnitInfo.Filename]);
6116     end;
6117   finally
6118     MissingClasses.Free;
6119     if ReferencesLocked then begin
6120       if Project1<>nil then
6121         Project1.UnlockUnitComponentDependencies;
6122     end;
6123   end;
6124 
6125   NewComponent:=AnUnitInfo.Component;
6126   // create the designer (if not already done)
6127   if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then
6128     FormEditor1.ClearSelection;
6129   {$IFDEF IDE_DEBUG}
6130   DebugLn('SUCCESS: streaming lfm="',LFMBuf.Filename,'"');
6131   {$ENDIF}
6132   AnUnitInfo.ComponentName:=NewComponent.Name;
6133   AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName;
6134   DesignerForm := nil;
6135   MainIDE.LastFormActivated := nil;
6136   if not (ofLoadHiddenResource in OpenFlags) then
6137   begin
6138     DesignerForm := FormEditor1.GetDesignerForm(NewComponent);
6139     if (DesignerForm=nil) or (DesignerForm.Designer=nil) then
6140       DesignerForm := MainIDE.CreateDesignerForComponent(AnUnitInfo,NewComponent);
6141   end;
6142 
6143   // select the new form (object inspector, formeditor, control selection)
6144   if (DesignerForm <> nil)
6145   and ([ofProjectLoading,ofLoadHiddenResource] * OpenFlags=[]) then
6146   begin
6147     MainIDE.DisplayState := dsForm;
6148     GlobalDesignHook.LookupRoot := NewComponent;
6149     TheControlSelection.AssignPersistent(NewComponent);
6150   end;
6151 
6152   // show new form
6153   if DesignerForm <> nil then
6154   begin
6155     DesignerForm.ControlStyle := DesignerForm.ControlStyle - [csNoDesignVisible];
6156     if NewComponent is TControl then
6157       TControl(NewComponent).ControlStyle:= TControl(NewComponent).ControlStyle - [csNoDesignVisible];
6158     if (DesignerForm.WindowState in [wsMinimized]) then
6159     begin
6160       ARestoreVisible := DesignerForm.Visible;
6161       DesignerForm.Visible := False;
6162       DesignerForm.ShowOnTop;
6163       DesignerForm.Visible := ARestoreVisible;
6164       DesignerForm.WindowState := wsMinimized;
6165     end else
6166       if IDETabMaster = nil then
6167         LCLIntf.ShowWindow(DesignerForm.Handle, ShowCommands[AnUnitInfo.ComponentState]);
6168     MainIDE.LastFormActivated := DesignerForm;
6169   end;
6170 
6171   {$IFDEF IDE_DEBUG}
6172   debugln('[LoadLFM] LFM end');
6173   {$ENDIF}
6174   Result:=mrOk;
6175 end;
6176 
6177 function OpenComponent(const UnitFilename: string;
6178   OpenFlags: TOpenFlags; CloseFlags: TCloseFlags; out Component: TComponent): TModalResult;
6179 var
6180   AnUnitInfo: TUnitInfo;
6181   LFMFilename: String;
6182   UnitCode: TCodeBuffer;
6183   LFMCode: TCodeBuffer;
6184   AFilename: String;
6185 begin
6186   if Project1=nil then exit(mrCancel);
6187   // try to find a unit name without expaning the path. this is required if unit is virtual
6188   // in other case file name will be expanded with the wrong path
6189   AFilename := UnitFilename;
6190   AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename);
6191   if AnUnitInfo = nil then
6192   begin
6193     AFilename:=TrimAndExpandFilename(UnitFilename);
6194     if (AFilename='') or (not FileExistsInIDE(AFilename,[])) then begin
6195       DebugLn(['OpenComponent file not found ',AFilename]);
6196       exit(mrCancel);
6197     end;
6198     AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename);
6199   end;
6200   if (not (ofRevert in OpenFlags))
6201   and (AnUnitInfo<>nil) and (AnUnitInfo.Component<>nil) then begin
6202     // already open
6203     Component:=AnUnitInfo.Component;
6204     Result:=mrOk;
6205     exit;
6206   end;
6207 
6208   // ToDo: use UnitResources
6209   LFMFilename:=ChangeFileExt(AFilename,'.lfm');
6210   if not FileExistsInIDE(LFMFilename,[]) then
6211     LFMFilename:=ChangeFileExt(AFilename,'.dfm');
6212   if not FileExistsInIDE(LFMFilename,[]) then begin
6213     DebugLn(['OpenComponent file not found ',LFMFilename]);
6214     exit(mrCancel);
6215   end;
6216 
6217   // load unit source
6218   Result:=LoadCodeBuffer(UnitCode,AFilename,[lbfCheckIfText],true);
6219   if Result<>mrOk then begin
6220     debugln('OpenComponent Failed loading ',AFilename);
6221     exit;
6222   end;
6223 
6224   // create unit info
6225   if AnUnitInfo=nil then begin
6226     AnUnitInfo:=TUnitInfo.Create(UnitCode);
6227     AnUnitInfo.ReadUnitNameFromSource(true);
6228     Project1.AddFile(AnUnitInfo,false);
6229   end;
6230 
6231   // load lfm source
6232   Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true);
6233   if Result<>mrOk then begin
6234     debugln('OpenComponent Failed loading ',LFMFilename);
6235     exit;
6236   end;
6237 
6238   // load resource
6239   Result:=LoadLFM(AnUnitInfo,LFMCode,OpenFlags,CloseFlags);
6240   if Result<>mrOk then begin
6241     debugln('OpenComponent DoLoadLFM failed ',LFMFilename);
6242     exit;
6243   end;
6244 
6245   Component:=AnUnitInfo.Component;
6246   if Component<>nil then
6247     Result:=mrOk
6248   else
6249     Result:=mrCancel;
6250 end;
6251 
6252 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
6253 var
6254   LFMFilename, LFMClassName, LFMType, Ancestor, LFMComponentName: String;
6255   LFMCode, Code: TCodeBuffer;
6256   LoadFileFlags: TLoadBufferFlags;
6257   ClearOldInfo: Boolean;
6258   Tool: TCodeTool;
6259   Node: TCodeTreeNode;
6260   ListOfPFindContext: TFPList;
6261   i: Integer;
6262   Context: PFindContext;
6263 begin
6264   Result:=false;
6265   if AnUnitInfo.Component<>nil then
6266     exit(true); // a loaded resource is always uptodate
6267   if AnUnitInfo.IsVirtual then
6268     exit(true); // a new unit is always uptodate
6269   ListOfPFindContext:=nil;
6270   ClearOldInfo:=true;
6271   try
6272     // find lfm file
6273     if not FilenameIsPascalUnit(AnUnitInfo.Filename) then
6274       exit(true); // not a unit -> clear info
6275     LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(
6276       AnUnitInfo.Filename,true);
6277     if (LFMFilename='') or not FileExistsCached(LFMFilename) then
6278       exit(true); // no lfm -> clear info
6279   finally
6280     if ClearOldInfo then begin
6281       AnUnitInfo.ResourceBaseClass:=pfcbcNone;
6282       AnUnitInfo.ComponentName:='';
6283       AnUnitInfo.ComponentResourceName:='';
6284     end;
6285   end;
6286   try
6287     if (CompareFileExt(LFMFilename,'lfm')<>0) then
6288     begin
6289       // no lfm format -> keep old info
6290       exit(true);
6291     end;
6292     // clear old info
6293     AnUnitInfo.ResourceBaseClass:=pfcbcNone;
6294     AnUnitInfo.ComponentName:='';
6295     AnUnitInfo.ComponentResourceName:='';
6296     // load lfm
6297     LoadFileFlags:=[lbfUpdateFromDisk,lbfCheckIfText];
6298     if Quiet then
6299       Include(LoadFileFlags,lbfQuiet);
6300     if LoadCodeBuffer(LFMCode,LFMFilename,LoadFileFlags,false)<>mrOk then
6301       exit; // lfm read error
6302     // read lfm header
6303     ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
6304     if LFMClassName='' then
6305       exit; // lfm syntax error
6306 
6307     // LFM component name
6308     AnUnitInfo.ComponentName:=LFMComponentName;
6309     AnUnitInfo.ComponentResourceName:=LFMComponentName;
6310 
6311     // check ancestors
6312     if LoadCodeBuffer(Code,AnUnitInfo.Filename,LoadFileFlags,false)<>mrOk then
6313       exit; // pas read error
6314     CodeToolBoss.Explore(Code,Tool,false,true);
6315     if Tool=nil then
6316       exit; // pas load error
6317     try
6318       Node:=Tool.FindDeclarationNodeInInterface(LFMClassName,true);
6319       if Node=nil then
6320         exit(Tool.FindImplementationNode<>nil); // class not found, reliable if whole interface was read
6321 
6322       if (Node=nil) or (Node.Desc<>ctnTypeDefinition)
6323       or (Node.FirstChild=nil) or (Node.FirstChild.Desc<>ctnClass) then
6324         exit(true); // this is not a class
6325       Tool.FindClassAndAncestors(Node.FirstChild,ListOfPFindContext,false);
6326       if ListOfPFindContext=nil then
6327         exit; // ancestor not found -> probably syntax error
6328 
6329       for i:=0 to ListOfPFindContext.Count-1 do begin
6330         Context:=PFindContext(ListOfPFindContext[i]);
6331         Ancestor:=UpperCase(Context^.Tool.ExtractClassName(Context^.Node,false));
6332         if (Ancestor='TFORM') then begin
6333           AnUnitInfo.ResourceBaseClass:=pfcbcForm;
6334           exit(true);
6335         end else if (Ancestor='TCUSTOMFORM') then begin
6336           AnUnitInfo.ResourceBaseClass:=pfcbcCustomForm;
6337           exit(true);
6338         end else if Ancestor='TDATAMODULE' then begin
6339           AnUnitInfo.ResourceBaseClass:=pfcbcDataModule;
6340           exit(true);
6341         end else if (Ancestor='TFRAME') or (Ancestor='TCUSTOMFRAME') then begin
6342           AnUnitInfo.ResourceBaseClass:=pfcbcFrame;
6343           exit(true);
6344         end else if Ancestor='TCOMPONENT' then
6345           exit(true);
6346       end;
6347     except
6348       exit; // syntax error or unit not found
6349     end;
6350   finally
6351     FreeListOfPFindContext(ListOfPFindContext);
6352   end;
6353 end;
6354 
6355 function FindBaseComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName,
6356   DescendantClassName: string; out AComponentClass: TComponentClass): boolean;
6357 // returns false if an error occured
6358 // Important: returns true even if AComponentClass=nil
6359 begin
6360   AComponentClass:=nil;
6361   // find the ancestor class
6362   if AnUnitInfo.UnitResourceFileformat<>nil then
6363   begin
6364     AComponentClass:=AnUnitInfo.UnitResourceFileformat.FindComponentClass(AComponentClassName);
6365     if AComponentClass<>nil then
6366       exit(true);
6367   end;
6368   if AComponentClassName<>'' then begin
6369     if (DescendantClassName<>'')
6370     and (SysUtils.CompareText(AComponentClassName,'TCustomForm')=0) then begin
6371       // this is a common user mistake
6372       IDEMessageDialog(lisCodeTemplError,
6373         Format(lisTheResourceClassDescendsFromProbablyThisIsATypoFor,
6374                [DescendantClassName, AComponentClassName]),
6375         mtError,[mbCancel]);
6376       Result:=false;
6377       exit;
6378     end else if (DescendantClassName<>'')
6379     and (SysUtils.CompareText(AComponentClassName,'TComponent')=0) then begin
6380       // this is not yet implemented
6381       IDEMessageDialog(lisCodeTemplError,
6382         Format(lisUnableToOpenDesignerTheClassDoesNotDescendFromADes,
6383                [LineEnding, DescendantClassName]),
6384         mtError,[mbCancel]);
6385       Result:=false;
6386       exit;
6387     end else begin
6388       // search in the registered base classes
6389       AComponentClass:=FormEditor1.FindDesignerBaseClassByName(AComponentClassName,true);
6390     end;
6391   end else begin
6392     // default is TForm
6393     AComponentClass:=BaseFormEditor1.StandardDesignerBaseClasses[DesignerBaseClassId_TForm];
6394   end;
6395   Result:=true;
6396 end;
6397 
6398 function LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo;
6399   const aComponentClassName: string;
6400   OpenFlags: TOpenFlags;
6401   out AncestorClass: TComponentClass;
6402   out AncestorUnitInfo: TUnitInfo): TModalResult;
6403 var
6404   AncestorClassName, IgnoreBtnText: String;
6405   CodeBuf: TCodeBuffer;
6406   GrandAncestorClass, DefAncestorClass: TComponentClass;
6407 begin
6408   AncestorClassName:='';
6409   AncestorClass:=nil;
6410   AncestorUnitInfo:=nil;
6411 
6412   // find the ancestor type in the source
6413   if AnUnitInfo.Source=nil then begin
6414     Result:=LoadCodeBuffer(CodeBuf,AnUnitInfo.Filename,
6415                            [lbfUpdateFromDisk,lbfCheckIfText],true);
6416     if Result<>mrOk then exit;
6417     AnUnitInfo.Source:=CodeBuf;
6418   end;
6419   if not CodeToolBoss.FindFormAncestor(AnUnitInfo.Source,aComponentClassName,
6420                                        AncestorClassName,true)
6421   then begin
6422     DebugLn('LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" ClassName=',aComponentClassName,'. Unable to find ancestor class: ',CodeToolBoss.ErrorMessage);
6423   end;
6424 
6425   // try the base designer classes
6426   if not FindBaseComponentClass(AnUnitInfo,AncestorClassName,
6427     aComponentClassName,AncestorClass) then
6428   begin
6429     DebugLn(['LoadAncestorDependencyHidden FindUnitComponentClass failed for AncestorClassName=',AncestorClassName]);
6430     exit(mrCancel);
6431   end;
6432 
6433   // try loading the ancestor first (unit, lfm and component instance)
6434 
6435   if AnUnitInfo.UnitResourceFileformat<>nil then
6436     DefAncestorClass:=AnUnitInfo.UnitResourceFileformat.DefaultComponentClass
6437   else
6438     DefAncestorClass:=nil;
6439   // use TForm as default ancestor
6440   if DefAncestorClass=nil then
6441     DefAncestorClass:=BaseFormEditor1.StandardDesignerBaseClasses[DesignerBaseClassId_TForm];
6442 
6443   if (AncestorClass=nil) then begin
6444     IgnoreBtnText:='';
6445     if DefAncestorClass<>nil then
6446       IgnoreBtnText:=Format(lisIgnoreUseAsAncestor, [DefAncestorClass.ClassName]);
6447 
6448     Result:=LoadComponentDependencyHidden(AnUnitInfo,AncestorClassName,
6449              OpenFlags,false,AncestorClass,AncestorUnitInfo,GrandAncestorClass,
6450              IgnoreBtnText);
6451     if Result<>mrOk then begin
6452       DebugLn(['LoadAncestorDependencyHidden DoLoadComponentDependencyHidden failed AnUnitInfo=',AnUnitInfo.Filename]);
6453     end;
6454     case  Result of
6455     mrAbort: exit;
6456     mrOk: ;
6457     mrIgnore:
6458       AncestorUnitInfo:=nil;
6459     else
6460       // cancel
6461       Result:=mrCancel;
6462       exit;
6463     end;
6464   end;
6465 
6466   //DebugLn('LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" AncestorClassName=',AncestorClassName,' AncestorClass=',dbgsName(AncestorClass));
6467   if AncestorClass=nil then
6468     AncestorClass:=DefAncestorClass;
6469   Result:=mrOk;
6470 end;
6471 
6472 function FindComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName: string;
6473   Quiet: boolean; out ComponentUnitInfo: TUnitInfo; out AComponentClass: TComponentClass;
6474   out LFMFilename: string; out AncestorClass: TComponentClass): TModalResult;
6475 { Possible results:
6476   mrOk:
6477    - AComponentClass<>nil and ComponentUnitInfo<>nil
6478       designer component
6479    - AComponentClass<>nil and ComponentUnitInfo=nil
6480       registered componentclass
6481    - LFMFilename<>''
6482       lfm of an used unit
6483    - AncestorClass<>nil
6484       componentclass does not exist, but the ancestor is a registered class
6485   mrCancel:
6486     not found
6487   mrAbort:
6488     not found, error already shown
6489 }
6490 var
6491   CTErrorMsg: String;
6492   CTErrorCode: TCodeBuffer;
6493   CTErrorLine: Integer;
6494   CTErrorCol: Integer;
6495 
6496   procedure StoreCodetoolsError;
6497   begin
6498     {$IFDEF VerboseLFMSearch}
6499     debugln(['  StoreCodetoolsError: ',CodeToolBoss.ErrorMessage]);
6500     {$ENDIF}
6501     if CTErrorMsg<>'' then exit;
6502     if CodeToolBoss.ErrorMessage<>'' then begin
6503       CTErrorMsg:=CodeToolBoss.ErrorMessage;
6504       CTErrorCode:=CodeToolBoss.ErrorCode;
6505       CTErrorLine:=CodeToolBoss.ErrorLine;
6506       CTErrorCol:=CodeToolBoss.ErrorColumn;
6507     end;
6508   end;
6509 
6510   function TryUnitComponent(const UnitFilename: string;
6511     out TheModalResult: TModalResult): boolean;
6512   // returns true if the unit contains the component class and sets
6513   // TheModalResult to the result of the loading
6514   var
6515     CurUnitInfo: TUnitInfo;
6516   begin
6517     {$IFDEF VerboseLFMSearch}
6518     debugln(['  TryUnitComponent UnitFilename="',UnitFilename,'"']);
6519     {$ENDIF}
6520     Result:=false;
6521     TheModalResult:=mrCancel;
6522     if not FilenameIsPascalUnit(UnitFilename) then exit;
6523 
6524     CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6525     if (CurUnitInfo=nil) or (CurUnitInfo.Component=nil) then exit;
6526     // unit with loaded component found -> check if it is the right one
6527     //DebugLn(['FindComponentClass unit with a component found CurUnitInfo=',CurUnitInfo.Filename,' ',dbgsName(CurUnitInfo.Component)]);
6528     if SysUtils.CompareText(CurUnitInfo.Component.ClassName,AComponentClassName)<>0
6529     then exit;
6530     // component found (it was already loaded)
6531     ComponentUnitInfo:=CurUnitInfo;
6532     AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6533     TheModalResult:=mrOk;
6534     Result:=true;
6535   end;
6536 
6537   function TryRegisteredClasses(aClassName: string;
6538     out FoundComponentClass: TComponentClass;
6539     out TheModalResult: TModalResult): boolean;
6540   var
6541     RegComp: TRegisteredComponent;
6542   begin
6543     {$IFDEF VerboseLFMSearch}
6544     debugln(['  TryRegisteredClasses aClassName="',aClassName,'"']);
6545     {$ENDIF}
6546     Result:=false;
6547     TheModalResult:=mrCancel;
6548     FoundComponentClass:=nil;
6549     if AnUnitInfo.UnitResourceFileformat<>nil then
6550       FoundComponentClass:=AnUnitInfo.UnitResourceFileformat.FindComponentClass(aClassName);
6551     if FoundComponentClass=nil then
6552     begin
6553       RegComp:=IDEComponentPalette.FindComponent(aClassName);
6554       if RegComp<>nil then
6555         FoundComponentClass:=RegComp.ComponentClass;
6556     end;
6557     if FoundComponentClass=nil then
6558       FoundComponentClass:=FormEditor1.FindDesignerBaseClassByName(aClassName,true);
6559     if FoundComponentClass<>nil then begin
6560       DebugLn(['FindComponentClass.TryRegisteredClasses found: ',FoundComponentClass.ClassName]);
6561       TheModalResult:=mrOk;
6562       Result:=true;
6563     end;
6564   end;
6565 
6566   function TryLFM(const UnitFilename, AClassName: string;
6567     out TheModalResult: TModalResult): boolean;
6568   var
6569     CurLFMFilename: String;
6570     LFMCode: TCodeBuffer;
6571     LFMClassName: String;
6572     LFMType: String;
6573   begin
6574     {$IFDEF VerboseLFMSearch}
6575     debugln(['  TryLFM UnitFilename="',UnitFilename,'" AClassName=',AClassName]);
6576     {$ENDIF}
6577     Result:=false;
6578     TheModalResult:=mrCancel;
6579     if not FilenameIsPascalSource(UnitFilename) then
6580     begin
6581       {$IFDEF VerboseLFMSearch}
6582       debugln(['  TryLFM UnitFilename="',UnitFilename,'" is not a unit']);
6583       {$ENDIF}
6584       exit;
6585     end;
6586     // ToDo: use UnitResources
6587     CurLFMFilename:=ChangeFileExt(UnitFilename,'.lfm');
6588     if not FileExistsCached(CurLFMFilename) then
6589     begin
6590       {$IFDEF VerboseLFMSearch}
6591       debugln(['  TryLFM CurLFMFilename="',CurLFMFilename,'" does not exist']);
6592       {$ENDIF}
6593       CurLFMFilename:=ChangeFileExt(UnitFilename,'.dfm');
6594       if not FileExistsCached(CurLFMFilename) then
6595       begin
6596         {$IFDEF VerboseLFMSearch}
6597         debugln(['  TryLFM CurLFMFilename="',CurLFMFilename,'" does not exist']);
6598         {$ENDIF}
6599         exit;
6600       end;
6601     end;
6602     // load the lfm file
6603     TheModalResult:=LoadCodeBuffer(LFMCode,CurLFMFilename,[lbfCheckIfText],true);
6604     if TheModalResult<>mrOk then
6605     begin
6606       debugln('FindComponentClass Failed loading ',CurLFMFilename);
6607       exit;
6608     end;
6609     // read the LFM classname
6610     ReadLFMHeader(LFMCode.Source,LFMClassName,LFMType);
6611     if LFMType='' then ;
6612     if SysUtils.CompareText(LFMClassName,AClassName)<>0 then
6613     begin
6614       {$IFDEF VerboseLFMSearch}
6615       debugln(['  TryLFM CurLFMFilename="',CurLFMFilename,'" LFMClassName="',LFMClassName,'" does not match']);
6616       {$ENDIF}
6617       exit;
6618     end;
6619 
6620     // .lfm found
6621     LFMFilename:=CurLFMFilename;
6622     Result:=true;
6623   end;
6624 
6625   function TryFindDeclaration(out TheModalResult: TModalResult): boolean;
6626   var
6627     Tool: TCodeTool;
6628 
6629     function FindTypeNode(Node: TCodeTreeNode; Level: integer): TCodeTreeNode;
6630     var
6631       TypeNode: TCodeTreeNode;
6632       Child: TCodeTreeNode;
6633     begin
6634       Result:=nil;
6635       if Node=nil then exit;
6636       if Node.Desc=ctnVarDefinition then begin
6637         TypeNode:=Tool.FindTypeNodeOfDefinition(Node);
6638         if (TypeNode=nil) or (TypeNode.Desc<>ctnIdentifier) then exit;
6639         if Tool.CompareSrcIdentifiers(TypeNode.StartPos,PChar(AComponentClassName))
6640         then exit(TypeNode);
6641       end else if Node.Desc=ctnTypeDefinition then begin
6642         if Tool.CompareSrcIdentifiers(Node.StartPos,PChar(AComponentClassName))
6643         then exit(Node);
6644       end;
6645       // increase level on identifier nodes
6646       if Node.Desc in AllIdentifierDefinitions then begin
6647         if Level=1 then exit; // ignore nested vars
6648         inc(Level);
6649       end;
6650       Child:=Node.FirstChild;
6651       while Child<>nil do begin
6652         Result:=FindTypeNode(Child,Level);
6653         if Result<>nil then exit;
6654         Child:=Child.NextBrother;
6655       end;
6656     end;
6657 
6658   var
6659     Code: TCodeBuffer;
6660     Params: TFindDeclarationParams;
6661     NewNode: TCodeTreeNode;
6662     NewTool: TFindDeclarationTool;
6663     InheritedNode: TCodeTreeNode;
6664     ClassNode: TCodeTreeNode;
6665     AncestorNode: TCodeTreeNode;
6666     AncestorClassName: String;
6667     Node: TCodeTreeNode;
6668     ok: Boolean;
6669   begin
6670     Result:=false;
6671     TheModalResult:=mrCancel;
6672     // parse interface current unit
6673     Code:=CodeToolBoss.LoadFile(AnUnitInfo.Filename,false,false);
6674     if Code=nil then begin
6675       debugln(['FindComponentClass unable to load ',AnUnitInfo.Filename]);
6676       exit;
6677     end;
6678     if not CodeToolBoss.Explore(Code,Tool,false,true) then begin
6679       {$IFDEF VerboseLFMSearch}
6680       debugln(['  CodeToolBoss.Explore failed: ',Code.Filename]);
6681       {$ENDIF}
6682       StoreCodetoolsError;
6683       exit;
6684     end;
6685     Params:=TFindDeclarationParams.Create;
6686     try
6687       ok:=false;
6688       try
6689         // search a class reference in the unit
6690         Node:=Tool.FindInterfaceNode;
6691         if Node=nil then
6692           Node:=Tool.Tree.Root;
6693         Node:=FindTypeNode(Node,0);
6694         if Node=nil then begin
6695           debugln('FindComponentClass Failed finding reference of ',AComponentClassName,' in ',Code.Filename);
6696           exit;
6697         end;
6698         if Node.Desc=ctnIdentifier then begin
6699           //debugln(['TryFindDeclaration found reference of ',AComponentClassName,' at ',Tool.CleanPosToStr(Node.StartPos)]);
6700           Params.ContextNode:=Node;
6701           Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound,
6702                          fdfExceptionOnPredefinedIdent,
6703                          fdfTopLvlResolving,fdfSearchInAncestors,
6704                          fdfIgnoreCurContextNode];
6705           Params.SetIdentifier(Tool,@Tool.Src[Node.StartPos],nil);
6706           if not Tool.FindIdentifierInContext(Params) then begin
6707             debugln(['FindComponentClass find declaration failed at ',Tool.CleanPosToStr(Node.StartPos,true)]);
6708             exit;
6709           end;
6710           NewNode:=Params.NewNode;
6711           NewTool:=Params.NewCodeTool;
6712         end else begin
6713           NewNode:=Node;
6714           NewTool:=Tool;
6715         end;
6716         ok:=true;
6717       except
6718         on E: Exception do
6719           CodeToolBoss.HandleException(E);
6720       end;
6721       if not ok then begin
6722         {$IFDEF VerboseLFMSearch}
6723         debugln(['  find declaration failed.']);
6724         {$ENDIF}
6725         StoreCodetoolsError;
6726         exit;
6727       end;
6728       // declaration found
6729       ClassNode:=NewNode.FirstChild;
6730       if (NewNode.Desc<>ctnTypeDefinition)
6731       or (ClassNode=nil) or (ClassNode.Desc<>ctnClass) then
6732       begin
6733         debugln(['FindComponentClass ',AComponentClassName,' is not a class at ',NewTool.CleanPosToStr(NewNode.StartPos,true)]);
6734         exit;
6735       end;
6736       // find inheritance list
6737       InheritedNode:=ClassNode.FirstChild;
6738       while (InheritedNode<>nil) and (InheritedNode.Desc<>ctnClassInheritance) do
6739         InheritedNode:=InheritedNode.NextBrother;
6740       if (InheritedNode=nil) or (InheritedNode.FirstChild=nil) then begin
6741         debugln(['FindComponentClass ',AComponentClassName,' is not a TComponent at ',NewTool.CleanPosToStr(NewNode.StartPos,true)]);
6742         exit;
6743       end;
6744       AncestorNode:=InheritedNode.FirstChild;
6745       AncestorClassName:=GetIdentifier(@NewTool.Src[AncestorNode.StartPos]);
6746       //debugln(['TryFindDeclaration declaration of ',AComponentClassName,' found at ',NewTool.CleanPosToStr(NewNode.StartPos),' ancestor="',AncestorClassName,'"']);
6747 
6748       // try unit component
6749       if TryUnitComponent(NewTool.MainFilename,TheModalResult) then
6750         exit(true);
6751 
6752       // try lfm
6753       if TryLFM(NewTool.MainFilename,AComponentClassName,TheModalResult) then
6754         exit(true);
6755 
6756       // search ancestor in registered classes
6757       if TryRegisteredClasses(AncestorClassName,AncestorClass,TheModalResult) then
6758         exit(true);
6759 
6760       {$IFDEF VerboseLFMSearch}
6761       debugln(['TryFindDeclaration declaration of ',AComponentClassName,' found at ',NewTool.CleanPosToStr(NewNode.StartPos),' Ancestor="',AncestorClassName,'", but no lfm and no registered class found']);
6762       {$ENDIF}
6763     finally
6764       Params.Free;
6765     end;
6766   end;
6767 
6768   function TryUsedUnitInterface(UnitFilename: string; out TheModalResult: TModalResult): boolean;
6769   var
6770     Code: TCodeBuffer;
6771     AncestorClassName: string;
6772   begin
6773     {$IFDEF VerboseLFMSearch}
6774     debugln(['  TryUsedUnitInterface UnitFilename="',UnitFilename,'"']);
6775     {$ENDIF}
6776     Result:=false;
6777     TheModalResult:=mrCancel;
6778     if not FilenameIsPascalSource(UnitFilename) then
6779     begin
6780       {$IFDEF VerboseLFMSearch}
6781       debugln(['  TryUsedUnitInterface UnitFilename="',UnitFilename,'" is not a unit']);
6782       {$ENDIF}
6783       exit;
6784     end;
6785     AncestorClassName:='';
6786     Code:=CodeToolBoss.LoadFile(UnitFilename,true,false);
6787     if Code=nil then begin
6788       debugln(['FindComponentClass unable to load ',AnUnitInfo.Filename]);
6789       exit;
6790     end;
6791     if not CodeToolBoss.FindFormAncestor(Code,AComponentClassName,AncestorClassName,true) then
6792     begin
6793       {$IFDEF VerboseLFMSearch}
6794       debugln(['  TryUsedUnitInterface FindFormAncestor failed for "',AComponentClassName,'"']);
6795       {$ENDIF}
6796       StoreCodetoolsError;
6797       exit;
6798     end;
6799     if AncestorClassName='' then begin
6800       {$IFDEF VerboseLFMSearch}
6801       debugln(['  TryUsedUnitInterface FindFormAncestor failed silently for "',AComponentClassName,'"']);
6802       {$ENDIF}
6803       exit;
6804     end;
6805     if TryRegisteredClasses(AncestorClassName,AncestorClass,TheModalResult) then
6806       exit(true);
6807   end;
6808 
6809 var
6810   UsedUnitFilenames: TStrings;
6811   i: Integer;
6812 begin
6813   Result:=mrCancel;
6814   AComponentClass:=nil;
6815   ComponentUnitInfo:=nil;
6816   AncestorClass:=nil;
6817   LFMFilename:='';
6818   CTErrorMsg:='';
6819   CTErrorCode:=nil;
6820   CTErrorLine:=0;
6821   CTErrorCol:=0;
6822 
6823   if not IsValidIdent(AComponentClassName) then
6824   begin
6825     DebugLn(['FindComponentClass invalid component class name "',AComponentClassName,'"']);
6826     exit(mrCancel);
6827   end;
6828 
6829   // search component lfm
6830   {$ifdef VerboseFormEditor}
6831   debugln('FindComponentClass START ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName);
6832   {$endif}
6833   // first search the resource of ComponentUnitInfo
6834   if AnUnitInfo<>nil then begin
6835     if TryUnitComponent(AnUnitInfo.Filename,Result) then exit;
6836   end;
6837 
6838   // then try registered global classes
6839   if TryRegisteredClasses(AComponentClassName,AComponentClass,Result) then exit;
6840 
6841   // search in used units
6842   UsedUnitFilenames:=nil;
6843   try
6844     if not CodeToolBoss.FindUsedUnitFiles(AnUnitInfo.Source,UsedUnitFilenames)
6845     then begin
6846       MainIDE.DoJumpToCodeToolBossError;
6847       Result:=mrCancel;
6848       exit;
6849     end;
6850 
6851     {$IFDEF VerboseLFMSearch}
6852     if (UsedUnitFilenames=nil) or (UsedUnitFilenames.Count=0) then
6853       debugln(['FindComponentClass unit has no main uses']);
6854     {$ENDIF}
6855 
6856     if (UsedUnitFilenames<>nil) then begin
6857       // search for every used unit the .lfm file
6858       for i:=UsedUnitFilenames.Count-1 downto 0 do begin
6859         if TryLFM(UsedUnitFilenames[i],AComponentClassName,Result) then exit;
6860       end;
6861       // search class via codetools
6862       if TryFindDeclaration(Result) then exit;
6863       // search the class in every used unit
6864       for i:=UsedUnitFilenames.Count-1 downto 0 do begin
6865         if TryUsedUnitInterface(UsedUnitFilenames[i],Result) then exit;
6866       end;
6867     end;
6868   finally
6869     UsedUnitFilenames.Free;
6870   end;
6871 
6872   // not found
6873   if Quiet then exit(mrCancel);
6874 
6875   // show codetool error
6876   if (CTErrorMsg<>'') and (not Quiet) then begin
6877     CodeToolBoss.SetError(20170421203251,CTErrorCode,CTErrorLine,CTErrorCol,CTErrorMsg);
6878     MainIDE.DoJumpToCodeToolBossError;
6879     Result:=mrAbort;
6880     exit;
6881   end;
6882 
6883   // just not found
6884   Result:=mrCancel;
6885 end;
6886 
6887 function LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo;
6888   const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean;
6889   out AComponentClass: TComponentClass; out ComponentUnitInfo: TUnitInfo;
6890   out AncestorClass: TComponentClass; const IgnoreBtnText: string): TModalResult;
6891 { Possible results:
6892   mrOk:
6893    - AComponentClass<>nil and ComponentUnitInfo<>nil
6894       designer component
6895    - AComponentClass<>nil and ComponentUnitInfo=nil
6896       registered componentclass
6897    - Only for MustHaveLFM=false: AncestorClass<>nil
6898       componentclass does not exist, but the ancestor is a registered class
6899   mrCancel:
6900     not found, skip this form
6901   mrAbort:
6902     not found, user wants to stop all pending operations
6903   mrIgnore:
6904     not found, user wants to skip this step and continue
6905 }
6906 
6907   function TryLFM(LFMFilename: string; out TheModalResult: TModalResult): boolean;
6908   var
6909     UnitFilename: String;
6910     CurUnitInfo: TUnitInfo;
6911     LFMCode: TCodeBuffer;
6912     LFMClassName: String;
6913     LFMType: String;
6914     UnitCode: TCodeBuffer;
6915   begin
6916     Result:=false;
6917     TheModalResult:=mrCancel;
6918     // load lfm
6919     TheModalResult:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true);
6920     if TheModalResult<>mrOk then begin
6921       {$IFDEF VerboseLFMSearch}
6922       debugln(['  TryLFM LoadCodeBuffer failed ',LFMFilename]);
6923       {$ENDIF}
6924       exit(TheModalResult=mrAbort);
6925     end;
6926     // check if the unit component is already loaded
6927     UnitFilename:=ChangeFileExt(LFMFilename,'.pas');
6928     CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6929     if CurUnitInfo=nil then begin
6930       UnitFilename:=ChangeFileExt(LFMFilename,'.pp');
6931       CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6932     end;
6933     ReadLFMHeader(LFMCode.Source,LFMClassName,LFMType);
6934     if CurUnitInfo<>nil then begin
6935       if (CurUnitInfo.Component<>nil) then begin
6936         // component already loaded
6937         if SysUtils.CompareText(CurUnitInfo.Component.ClassName,LFMClassName)<>0
6938         then begin
6939           {$IFDEF VerboseLFMSearch}
6940           debugln(['  TryLFM ERROR lfmclass=',LFMClassName,' unit.component=',DbgSName(CurUnitInfo.Component)]);
6941           {$ENDIF}
6942           IDEMessageDialog('Error','Unable to load "'+LFMFilename+'".'
6943             +' The component '+DbgSName(CurUnitInfo.Component)
6944             +' is already loaded for unit "'+CurUnitInfo.Filename+'"'#13
6945             +'LFM contains a different class name "'+LFMClassName+'".',
6946             mtError,[mbCancel]);
6947           TheModalResult:=mrAbort;
6948           exit(true);
6949         end;
6950         ComponentUnitInfo:=CurUnitInfo;
6951         AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6952         TheModalResult:=mrOK;
6953         exit(true);
6954       end;
6955     end else begin
6956       // load unit source
6957       UnitFilename:=ChangeFileExt(LFMFilename,'.pas');
6958       if not FileExistsCached(UnitFilename) then
6959         UnitFilename:=ChangeFileExt(LFMFilename,'.pp');
6960       TheModalResult:=LoadCodeBuffer(UnitCode,UnitFilename,[lbfCheckIfText],true);
6961       if TheModalResult<>mrOk then exit(TheModalResult=mrAbort);
6962       // create unit info
6963       CurUnitInfo:=TUnitInfo.Create(UnitCode);
6964       CurUnitInfo.ReadUnitNameFromSource(true);
6965       Project1.AddFile(CurUnitInfo,false);
6966     end;
6967 
6968     // load resource hidden
6969     TheModalResult:=LoadLFM(CurUnitInfo,LFMCode,
6970                               Flags+[ofLoadHiddenResource],[]);
6971     if (TheModalResult=mrOk) then begin
6972       ComponentUnitInfo:=CurUnitInfo;
6973       AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6974       {$if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
6975       debugln('LoadComponentDependencyHidden Wanted=',AComponentClassName,' Class=',AComponentClass.ClassName);
6976       {$endif}
6977       TheModalResult:=mrOk;
6978       exit(true);
6979     end else begin
6980       debugln('LoadComponentDependencyHidden Failed to load component ',AComponentClassName);
6981       TheModalResult:=mrCancel;
6982     end;
6983   end;
6984 
6985 var
6986   Quiet, HideAbort: Boolean;
6987   LFMFilename, MsgText: string;
6988 begin
6989   Result:=mrCancel;
6990   AComponentClass:=nil;
6991   Quiet:=([ofProjectLoading,ofQuiet]*Flags<>[]);
6992   HideAbort:=not (ofProjectLoading in Flags);
6993 
6994   if not IsValidIdent(AComponentClassName) then
6995   begin
6996     DebugLn(['LoadComponentDependencyHidden invalid component class name "',AComponentClassName,'"']);
6997     exit(mrCancel);
6998   end;
6999 
7000   // check for cycles
7001   if AnUnitInfo.LoadingComponent then begin
7002     Result:=IDEQuestionDialogAb(lisCodeTemplError,
7003       Format(lisUnableToLoadTheComponentClassBecauseItDependsOnIts, [AComponentClassName]),
7004       mtError, [mrCancel, lisCancelLoadingThisComponent], HideAbort);
7005     exit;
7006   end;
7007 
7008   AnUnitInfo.LoadingComponent:=true;
7009   try
7010     // search component lfm
7011     {$if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
7012     debugln('LoadComponentDependencyHidden ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName,' AComponentClass=',dbgsName(AComponentClass));
7013     {$endif}
7014     Result:=FindComponentClass(AnUnitInfo,AComponentClassName,Quiet,
7015       ComponentUnitInfo,AComponentClass,LFMFilename,AncestorClass);
7016     { $if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
7017     debugln('LoadComponentDependencyHidden ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName,' AComponentClass=',dbgsName(AComponentClass),' AncestorClass=',DbgSName(AncestorClass),' LFMFilename=',LFMFilename);
7018     { $endif}
7019 
7020     //- AComponentClass<>nil and ComponentUnitInfo<>nil
7021     //   designer component
7022     //- AComponentClass<>nil and ComponentUnitInfo=nil
7023     //   registered componentclass
7024     //- LFMFilename<>''
7025     //   lfm of an used unit
7026     //- AncestorClass<>nil
7027     //   componentclass does not exist, but the ancestor is a registered class
7028 
7029     if (Result=mrOk) and (AComponentClass=nil) and (LFMFilename<>'') then begin
7030       TryLFM(LFMFilename,Result);
7031       exit;
7032     end;
7033 
7034     if MustHaveLFM and (AComponentClass=nil) then
7035       Result:=mrCancel;
7036     if Result=mrAbort then exit;
7037     if Result<>mrOk then begin
7038       MsgText:=Format(lisUnableToFindTheComponentClassItIsNotRegisteredViaR, [
7039           AComponentClassName, LineEnding, LineEnding, LineEnding, AnUnitInfo.Filename]);
7040       if IgnoreBtnText<>'' then
7041         Result:=IDEQuestionDialogAb(lisCodeTemplError, MsgText, mtError,
7042                   [mrCancel, lisCancelLoadingThisComponent,
7043                    mrIgnore, IgnoreBtnText], HideAbort)
7044       else
7045         Result:=IDEQuestionDialogAb(lisCodeTemplError, MsgText, mtError,
7046                   [mrCancel, lisCancelLoadingThisComponent], HideAbort);
7047     end;
7048   finally
7049     AnUnitInfo.LoadingComponent:=false;
7050   end;
7051 end;
7052 
7053 function LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer;
7054   const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult;
7055 begin
7056   if (Project1<>nil)
7057   and (Project1.UnitInfoWithFilename(AFilename,[pfsfOnlyEditorFiles])<>nil) then
7058     Exclude(Flags,lbfUpdateFromDisk);
7059   Result:=LoadCodeBuffer(ACodeBuffer,AFilename,Flags,ShowAbort);
7060 end;
7061 
7062 function CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
7063 
7064   procedure FreeUnusedComponents;
7065   var
7066     CompUnitInfo: TUnitInfo;
7067   begin
7068     CompUnitInfo:=Project1.FirstUnitWithComponent;
7069     Project1.UpdateUnitComponentDependencies;
7070     while CompUnitInfo<>nil do begin
7071       //DebugLn(['FreeUnusedComponents ',CompUnitInfo.Filename,' ',dbgsName(CompUnitInfo.Component),' UnitComponentIsUsed=',UnitComponentIsUsed(CompUnitInfo,true)]);
7072       if not UnitComponentIsUsed(CompUnitInfo,true) then begin
7073         // close the unit component
7074         CloseUnitComponent(CompUnitInfo,Flags);
7075         // this has recursively freed all components, so exit here
7076         exit;
7077       end;
7078       CompUnitInfo:=CompUnitInfo.NextUnitWithComponent;
7079     end;
7080   end;
7081 
7082 var
7083   OldDesigner: TIDesigner;
7084   AForm: TCustomForm;
7085   LookupRoot: TComponent;
7086   ComponentStillUsed: Boolean;
7087 begin
7088   LookupRoot:=AnUnitInfo.Component;
7089   if LookupRoot=nil then exit(mrOk);
7090   {$IFDEF VerboseIDEMultiForm}
7091   DebugLn(['CloseUnitComponent ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]);
7092   {$ENDIF}
7093 
7094   Project1.LockUnitComponentDependencies; // avoid circles
7095   try
7096     // save
7097     if (cfSaveFirst in Flags) and (AnUnitInfo.OpenEditorInfoCount > 0)
7098     and (not AnUnitInfo.IsReverting) then begin
7099       Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent,[sfCheckAmbiguousFiles]);
7100       if Result<>mrOk then begin
7101         DebugLn(['CloseUnitComponent DoSaveEditorFile failed']);
7102         exit;
7103       end;
7104     end;
7105 
7106     // close dependencies
7107     if cfCloseDependencies in Flags then begin
7108       {$IFDEF VerboseIDEMultiForm}
7109       DebugLn(['CloseUnitComponent cfCloseDependencies ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]);
7110       {$ENDIF}
7111       Result:=CloseDependingUnitComponents(AnUnitInfo,Flags);
7112       if Result<>mrOk then begin
7113         DebugLn(['CloseUnitComponent CloseDependingUnitComponents failed']);
7114         exit;
7115       end;
7116       // now only soft dependencies are left. The component can be freed.
7117     end;
7118 
7119     AForm:=FormEditor1.GetDesignerForm(LookupRoot);
7120     if AForm<>nil then
7121       OldDesigner:=AForm.Designer
7122     else
7123       OldDesigner:=nil;
7124     if MainIDE.LastFormActivated=AForm then
7125       MainIDE.LastFormActivated:=nil;
7126     ComponentStillUsed:=(not (cfCloseDependencies in Flags))
7127                         and UnitComponentIsUsed(AnUnitInfo,false);
7128     {$IFDEF VerboseTFrame}
7129     DebugLn(['CloseUnitComponent ',AnUnitInfo.Filename,' ComponentStillUsed=',ComponentStillUsed,' UnitComponentIsUsed=',UnitComponentIsUsed(AnUnitInfo,false),' ',dbgs(AnUnitInfo.Flags),' DepAncestor=',AnUnitInfo.FindUsedByComponentDependency([ucdtAncestor])<>nil,' DepInline=',AnUnitInfo.FindUsedByComponentDependency([ucdtInlineClass])<>nil]);
7130     {$ENDIF}
7131     if (OldDesigner=nil) then begin
7132       // hidden component
7133       //DebugLn(['CloseUnitComponent freeing hidden component without designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7134       if ComponentStillUsed then begin
7135         // hidden component is still used => keep it
7136         {$IFDEF VerboseIDEMultiForm}
7137         DebugLn(['CloseUnitComponent hidden component is still used => keep it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7138         {$ENDIF}
7139       end else begin
7140         // hidden component is not used => free it
7141         {$IFDEF VerboseIDEMultiForm}
7142         DebugLn(['CloseUnitComponent hidden component is not used => free it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7143         {$ENDIF}
7144         try
7145           FormEditor1.DeleteComponent(LookupRoot,true);
7146         finally
7147           AnUnitInfo.Component:=nil;
7148         end;
7149         FreeUnusedComponents;
7150       end;
7151     end else begin
7152       // component with designer
7153       AnUnitInfo.LoadedDesigner:=false;
7154       if ComponentStillUsed then begin
7155         // free designer, keep component hidden
7156         {$IFDEF VerboseIDEMultiForm}
7157         DebugLn(['CloseUnitComponent hiding component and freeing designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7158         {$ENDIF}
7159         OldDesigner.PrepareFreeDesigner(false);
7160       end else begin
7161         // free designer and design form
7162         {$IFDEF VerboseIDEMultiForm}
7163         DebugLn(['CloseUnitComponent freeing component and designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7164         {$ENDIF}
7165         try
7166           OldDesigner.PrepareFreeDesigner(true);
7167         finally
7168           AnUnitInfo.Component:=nil;
7169         end;
7170       end;
7171       Project1.InvalidateUnitComponentDesignerDependencies;
7172       FreeUnusedComponents;
7173     end;
7174   finally
7175     Project1.UnlockUnitComponentDependencies;
7176   end;
7177   Result:=mrOk;
7178 end;
7179 
7180 function CloseDependingUnitComponents(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
7181 var
7182   UserAsked: Boolean;
7183 
7184   function CloseNext(var ModResult: TModalresult;
7185     Types: TUnitCompDependencyTypes): boolean;
7186   var
7187     DependingUnitInfo: TUnitInfo;
7188     DependenciesFlags: TCloseFlags;
7189   begin
7190     ModResult:=mrOk;
7191 repeat
7192       DependingUnitInfo:=Project1.UnitUsingComponentUnit(AnUnitInfo,Types);
7193       if DependingUnitInfo=nil then break;
7194       if (not UserAsked) and (not (cfQuiet in Flags))
7195       and (not DependingUnitInfo.IsReverting) then begin
7196         // ToDo: collect in advance all components to close and show user the list
7197         ModResult:=IDEQuestionDialog('Close component?',
7198           'Close component '+dbgsName(DependingUnitInfo.Component)+'?',
7199           mtConfirmation,[mrYes,mrAbort]);
7200         if ModResult<>mrYes then exit(false);
7201         UserAsked:=true;
7202       end;
7203       // close recursively
7204       DependenciesFlags:=Flags+[cfCloseDependencies];
7205       if cfSaveDependencies in Flags then
7206         Include(DependenciesFlags,cfSaveFirst);
7207       ModResult:=CloseUnitComponent(DependingUnitInfo,DependenciesFlags);
7208       if ModResult<>mrOk then exit(false);
7209     until false;
7210     ModResult:=mrOk;
7211     Result:=true;
7212   end;
7213 
7214 begin
7215   Result:=mrOk;
7216   UserAsked:=false;
7217   Project1.LockUnitComponentDependencies;
7218   try
7219     // Important:
7220     // This function is called recursively.
7221     // It is important that first the hard, non cyclic dependencies
7222     // are freed in the correct order.
7223     // After that the soft, cyclic dependencies can be freed in any order.
7224 
7225     // first close all descendants recursively
7226     // This must happen in the right order (descendants before ancestor)
7227     if not CloseNext(Result,[ucdtAncestor]) then exit;
7228 
7229     // then close all nested descendants recursively
7230     // This must happen in the right order (nested descendants before ancestor)
7231     if not CloseNext(Result,[ucdtInlineClass]) then exit;
7232 
7233     // then close all referring components
7234     // These can build cycles and can be freed in any order.
7235     if not CloseNext(Result,[ucdtProperty]) then exit;
7236   finally
7237     Project1.UnlockUnitComponentDependencies;
7238   end;
7239   Result:=mrOk;
7240 end;
7241 
7242 function UnitComponentIsUsed(AnUnitInfo: TUnitInfo;
7243   CheckHasDesigner: boolean): boolean;
7244 // if CheckHasDesigner=true and AnUnitInfo has a designer (visible) return true
7245 // otherwise check if another unit needs AnUnitInfo
7246 var
7247   LookupRoot: TComponent;
7248 begin
7249   Result:=false;
7250   LookupRoot:=AnUnitInfo.Component;
7251   if LookupRoot=nil then exit;
7252   // check if a designer or another component uses this component
7253   Project1.UpdateUnitComponentDependencies;
7254   if Project1.UnitComponentIsUsed(AnUnitInfo,CheckHasDesigner) then
7255     exit(true);
7256   //DebugLn(['UnitComponentIsUsed ',AnUnitInfo.Filename,' ',dbgs(AnUnitInfo.Flags)]);
7257 end;
7258 
7259 function RemoveFilesFromProject(UnitInfos: TFPList): TModalResult;
7260 var
7261   AnUnitInfo: TUnitInfo;
7262   ShortUnitName, UnitPath: String;
7263   ObsoleteUnitPaths, ObsoleteIncPaths: String;
7264   i: Integer;
7265   Dummy: Boolean;
7266 begin
7267   Result:=mrOk;
7268   if UnitInfos=nil then exit;
7269   // check if something will change
7270   i:=UnitInfos.Count-1;
7271   while (i>=0) and (not TUnitInfo(UnitInfos[i]).IsPartOfProject) do dec(i);
7272   if i<0 then exit;
7273   // check ToolStatus
7274   if (MainIDE.ToolStatus in [itCodeTools,itCodeToolAborting]) then begin
7275     debugln('RemoveUnitsFromProject wrong ToolStatus ',dbgs(ord(MainIDE.ToolStatus)));
7276     exit;
7277   end;
7278   // commit changes from source editor to codetools
7279   SaveEditorChangesToCodeCache(nil);
7280 
7281   ObsoleteUnitPaths:='';
7282   ObsoleteIncPaths:='';
7283   Project1.BeginUpdate(true);
7284   try
7285     for i:=0 to UnitInfos.Count-1 do begin
7286       AnUnitInfo:=TUnitInfo(UnitInfos[i]);
7287       //debugln(['RemoveUnitsFromProject Unit ',AnUnitInfo.Filename]);
7288       if not AnUnitInfo.IsPartOfProject then continue;
7289       UnitPath:=ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename));
7290       AnUnitInfo.IsPartOfProject:=false;
7291       Project1.Modified:=true;
7292       if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin
7293         if FilenameIsAbsolute(AnUnitInfo.Filename) then
7294           ObsoleteUnitPaths:=MergeSearchPaths(ObsoleteUnitPaths,UnitPath);
7295         // remove from project's unit section
7296         if (Project1.MainUnitID>=0)
7297         and (pfMainUnitIsPascalSource in Project1.Flags)
7298         then begin
7299           ShortUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename);
7300           //debugln(['RemoveUnitsFromProject UnitName=',ShortUnitName]);
7301           if (ShortUnitName<>'') then begin
7302             Dummy:=CodeToolBoss.RemoveUnitFromAllUsesSections(
7303                                       Project1.MainUnitInfo.Source,ShortUnitName);
7304             if not Dummy then begin
7305               MainIDE.DoJumpToCodeToolBossError;
7306               exit(mrCancel);
7307             end;
7308           end;
7309         end;
7310         // remove CreateForm statement from project
7311         if (Project1.MainUnitID>=0)
7312         and (pfMainUnitHasCreateFormStatements in Project1.Flags)
7313         and (AnUnitInfo.ComponentName<>'') then begin
7314           Dummy:=Project1.RemoveCreateFormFromProjectFile(
7315               'T'+AnUnitInfo.ComponentName,AnUnitInfo.ComponentName);
7316           if not Dummy then begin
7317             MainIDE.DoJumpToCodeToolBossError;
7318             exit(mrCancel);
7319           end;
7320         end;
7321       end;
7322       if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then
7323         // include file
7324         if FilenameIsAbsolute(AnUnitInfo.Filename) then
7325           ObsoleteIncPaths:=MergeSearchPaths(ObsoleteIncPaths,UnitPath);
7326     end;
7327 
7328     // removed directories still used for ObsoleteUnitPaths, ObsoleteIncPaths
7329     AnUnitInfo:=Project1.FirstPartOfProject;
7330     while AnUnitInfo<>nil do begin
7331       if FilenameIsAbsolute(AnUnitInfo.Filename) then begin
7332         UnitPath:=ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename));
7333         if FilenameIsPascalUnit(AnUnitInfo.Filename) then
7334           ObsoleteUnitPaths:=RemoveSearchPaths(ObsoleteUnitPaths,UnitPath);
7335         if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then
7336           ObsoleteIncPaths:=RemoveSearchPaths(ObsoleteIncPaths,UnitPath);
7337       end;
7338       AnUnitInfo:=AnUnitInfo.NextPartOfProject;
7339     end;
7340 
7341     // check if compiler options contain paths of ObsoleteUnitPaths
7342     if ObsoleteUnitPaths<>'' then
7343       RemovePathFromBuildModes(ObsoleteUnitPaths, pcosUnitPath);
7344     // or paths of ObsoleteIncPaths
7345     if ObsoleteIncPaths<>'' then
7346       RemovePathFromBuildModes(ObsoleteIncPaths, pcosIncludePath);
7347 
7348   finally
7349     // all changes were handled automatically by events, just clear the logs
7350     CodeToolBoss.SourceCache.ClearAllSourceLogEntries;
7351     Project1.EndUpdate;
7352   end;
7353 end;
7354 
7355 // methods for open project, create project from source
7356 
7357 function CompleteLoadingProjectInfo: TModalResult;
7358 begin
7359   MainIDE.UpdateCaption;
7360   EnvironmentOptions.LastSavedProjectFile:=Project1.ProjectInfoFile;
7361   MainIDE.SaveEnvironment;
7362 
7363   MainBuildBoss.SetBuildTargetProject1(false);
7364 
7365   // load required packages
7366   PkgBoss.OpenProjectDependencies(Project1,true);
7367 
7368   Project1.DefineTemplates.AllChanged;
7369   //DebugLn('CompleteLoadingProjectInfo ',Project1.IDAsString);
7370   Project1.DefineTemplates.Active:=true;
7371 
7372   Result:=mrOk;
7373 end;
7374 
7375 // Methods for 'save project'
7376 
7377 function SaveProjectInfo(var Flags: TSaveFlags): TModalResult;
7378 var
7379   MainUnitInfo: TUnitInfo;
7380   MainUnitSrcEdit: TSourceEditor;
7381   DestFilename: String;
7382   SkipSavingMainSource: Boolean;
7383 begin
7384   Result:=mrOk;
7385   Project1.ActiveWindowIndexAtStart := SourceEditorManager.ActiveSourceWindowIndex;
7386 
7387   // update source notebook page names
7388   UpdateSourceNames;
7389 
7390   // find mainunit
7391   GetMainUnit(MainUnitInfo,MainUnitSrcEdit,true);
7392 
7393   // save project specific settings of the source editor
7394   SaveSourceEditorProjectSpecificSettings;
7395 
7396   if Project1.IsVirtual
7397   and (not (sfDoNotSaveVirtualFiles in Flags)) then
7398     Include(Flags,sfSaveAs);
7399   if ([sfSaveAs,sfSaveToTestDir]*Flags=[sfSaveAs]) then begin
7400     // let user choose a filename
7401     Result:=ShowSaveProjectAsDialog(sfSaveMainSourceAs in Flags);
7402     if Result<>mrOk then begin
7403       debugln(['Info: (lazarus) [SaveProjectInfo] ShowSaveProjectAsDialog failed']);
7404       exit;
7405     end;
7406     Flags:=Flags-[sfSaveAs,sfSaveMainSourceAs];
7407   end;
7408 
7409   // update HasResources information
7410   UpdateProjectResourceInfo;
7411 
7412   // save project info file
7413   //debugln(['SaveProjectInfo ',Project1.ProjectInfoFile,' Test=',sfSaveToTestDir in Flags,' Virt=',Project1.IsVirtual]);
7414   if (not (sfSaveToTestDir in Flags))
7415   and (not Project1.IsVirtual) then begin
7416     Result:=Project1.WriteProject([],'',EnvironmentOptions.BuildMatrixOptions);
7417     if Result=mrAbort then begin
7418       debugln(['Info: (lazarus) [SaveProjectInfo] Project1.WriteProject failed']);
7419       exit;
7420     end;
7421     EnvironmentOptions.LastSavedProjectFile:=Project1.ProjectInfoFile;
7422     IDEProtocolOpts.LastProjectLoadingCrashed := False;
7423     AddRecentProjectFile(Project1.ProjectInfoFile);
7424     MainIDE.SaveIncludeLinks;
7425     MainIDE.UpdateCaption;
7426   end;
7427 
7428   // save main source
7429   if (MainUnitInfo<>nil) and (not (sfDoNotSaveVirtualFiles in flags)) then
7430   begin
7431     if not (sfSaveToTestDir in Flags) then
7432       DestFilename := MainUnitInfo.Filename
7433     else
7434       DestFilename := MainBuildBoss.GetTestUnitFilename(MainUnitInfo);
7435 
7436     if MainUnitInfo.OpenEditorInfoCount > 0 then
7437     begin
7438       // loaded in source editor
7439       Result:=SaveEditorFile(MainUnitInfo.OpenEditorInfo[0].EditorComponent,
7440                [sfProjectSaving]+[sfSaveToTestDir,sfCheckAmbiguousFiles]*Flags);
7441       if Result=mrAbort then begin
7442         debugln(['Info: (lazarus) [SaveProjectInfo] SaveEditorFile MainUnitInfo failed "',DestFilename,'"']);
7443         exit;
7444       end;
7445     end else
7446     begin
7447       // not loaded in source editor (hidden)
7448       SkipSavingMainSource := false;
7449       if not (sfSaveToTestDir in Flags) and not MainUnitInfo.NeedsSaveToDisk then
7450         SkipSavingMainSource := true;
7451       if (not SkipSavingMainSource) and (MainUnitInfo.Source<>nil) then
7452       begin
7453         Result:=SaveCodeBufferToFile(MainUnitInfo.Source, DestFilename);
7454         if Result=mrAbort then begin
7455           debugln(['Info: (lazarus) [SaveProjectInfo] SaveEditorFile failed "',DestFilename,'"']);
7456           exit;
7457         end;
7458       end;
7459     end;
7460 
7461     // clear modified flags
7462     if not (sfSaveToTestDir in Flags) then
7463     begin
7464       if (Result=mrOk) then begin
7465         if MainUnitInfo<>nil then MainUnitInfo.ClearModifieds;
7466         if MainUnitSrcEdit<>nil then MainUnitSrcEdit.Modified:=false;
7467       end;
7468     end;
7469   end;
7470 end;
7471 
7472 procedure GetMainUnit(out MainUnitInfo: TUnitInfo;
7473   out MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean);
7474 begin
7475   MainUnitSrcEdit:=nil;
7476   if Project1.MainUnitID>=0 then begin
7477     MainUnitInfo:=Project1.MainUnitInfo;
7478     if MainUnitInfo.OpenEditorInfoCount > 0 then begin
7479       MainUnitSrcEdit := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent);
7480       if UpdateModified and MainUnitSrcEdit.Modified then
7481         MainUnitSrcEdit.UpdateCodeBuffer;
7482     end;
7483   end else
7484     MainUnitInfo:=nil;
7485 end;
7486 
7487 procedure SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo);
7488 var
7489   ASrcEdit: TSourceEditor;
7490 begin
7491   ASrcEdit := TSourceEditor(AnEditorInfo.EditorComponent);
7492   if ASrcEdit=nil then exit;
7493   AnEditorInfo.TopLine:=ASrcEdit.EditorComponent.TopLine;
7494   AnEditorInfo.CursorPos:=ASrcEdit.EditorComponent.CaretXY;
7495   AnEditorInfo.FoldState := ASrcEdit.EditorComponent.FoldState;
7496 end;
7497 
7498 procedure SaveSourceEditorProjectSpecificSettings;
7499 var
7500   i: Integer;
7501 begin
7502   for i := 0 to Project1.AllEditorsInfoCount - 1 do
7503     SaveSrcEditorProjectSpecificSettings(Project1.AllEditorsInfo[i]);
7504 end;
7505 
7506 procedure UpdateProjectResourceInfo;
7507 var
7508   AnUnitInfo: TUnitInfo;
7509   LFMFilename: String;
7510 begin
7511   AnUnitInfo:=Project1.FirstPartOfProject;
7512   while AnUnitInfo<>nil do begin
7513     if (not AnUnitInfo.HasResources)
7514     and (not AnUnitInfo.IsVirtual) and FilenameIsPascalUnit(AnUnitInfo.Filename)
7515     then begin
7516       LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm');
7517       if not FileExistsCached(LFMFilename) then
7518         LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.dfm');
7519       AnUnitInfo.HasResources:=FileExistsCached(LFMFilename);
7520     end;
7521     AnUnitInfo:=AnUnitInfo.NextPartOfProject;
7522   end;
7523 end;
7524 
7525 function ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult;
7526 var
7527   MainUnitSrcEdit: TSourceEditor;
7528   MainUnitInfo: TUnitInfo;
7529   SaveDialog: TIDESaveDialog;
7530   NewBuf, OldBuf: TCodeBuffer;
7531   TitleWasDefault: Boolean;
7532   NewLPIFilename, NewProgramFN, NewProgramName, AFilename, NewTargetFN: String;
7533   AText, ACaption, Ext: string;
7534   OldSourceCode, OldProjectDir, prDir: string;
7535   i: Integer;
7536 begin
7537   Project1.BeginUpdate(false);
7538   try
7539     OldProjectDir:=Project1.Directory;
7540 
7541     if Project1.MainUnitInfo = nil then
7542       UseMainSourceFile := False;
7543 
7544     SaveDialog:=IDESaveDialogClass.Create(nil);
7545     try
7546       InputHistories.ApplyFileDialogSettings(SaveDialog);
7547       AFilename:='';
7548       // build a nice project info filename suggestion
7549       if UseMainSourceFile and (Project1.MainUnitID>=0) then
7550         AFilename:=Project1.MainUnitInfo.Unit_Name;
7551       if AFilename='' then
7552         AFilename:=ExtractFileName(Project1.ProjectInfoFile);
7553       if AFilename='' then
7554         AFilename:=ExtractFileName(Project1.MainFilename);
7555       if AFilename='' then
7556         AFilename:=Trim(Project1.GetTitle);
7557       if AFilename='' then
7558         AFilename:='project1';
7559       Ext := LowerCase(ExtractFileExt(AFilename));
7560       if UseMainSourceFile then
7561       begin
7562         if (Ext = '') or (not FilenameIsPascalSource(AFilename)) then
7563           AFilename := ChangeFileExt(AFilename, '.pas');
7564       end else
7565       begin
7566         if (Ext = '') or FilenameIsPascalSource(AFilename) then
7567           AFilename := ChangeFileExt(AFilename, '.lpi');
7568       end;
7569       Ext := ExtractFileExt(AFilename);
7570       SaveDialog.Title := Format(lisSaveProject, [Project1.GetTitleOrName, Ext]);
7571       SaveDialog.FileName := AFilename;
7572       // Note: add *.* filter, so users can see the files in the target directory
7573       SaveDialog.Filter := '*' + Ext + '|' + '*' + Ext
7574          + '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
7575       SaveDialog.DefaultExt := Ext;
7576       if not Project1.IsVirtual then
7577         SaveDialog.InitialDir := Project1.Directory;
7578 
7579       repeat
7580         Result:=mrCancel;
7581         NewLPIFilename:='';     // the project info file name
7582         NewProgramName:='';     // the pascal program identifier
7583         NewProgramFN:='';       // the program source filename
7584 
7585         if not SaveDialog.Execute then
7586           exit;   // user cancels
7587         AFilename:=ExpandFileNameUTF8(SaveDialog.FileName);
7588         Assert(FilenameIsAbsolute(AFilename),'ShowSaveProjectAsDialog: buggy ExpandFileNameUTF8');
7589 
7590         // check program name
7591         NewProgramName:=ExtractFileNameOnly(AFilename);
7592         if (NewProgramName='') or (not IsValidUnitName(NewProgramName)) then begin
7593           Result:=IDEMessageDialog(lisInvalidProjectFilename,
7594             Format(lisisAnInvalidProjectNamePleaseChooseAnotherEGProject,[SaveDialog.Filename,LineEnding]),
7595             mtInformation,[mbRetry,mbAbort]);
7596           if Result=mrAbort then exit;
7597           continue; // try again
7598         end;
7599 
7600         // append default extension
7601         if UseMainSourceFile then
7602         begin
7603           NewLPIFilename:=ChangeFileExt(AFilename,'.lpi');
7604         end else
7605         begin
7606           NewLPIFilename:=AFilename;
7607           if ExtractFileExt(NewLPIFilename)='' then
7608             NewLPIFilename:=NewLPIFilename+'.lpi';
7609         end;
7610 
7611         // apply naming conventions
7612         // rename to lowercase is not needed for main source
7613 
7614         if Project1.MainUnitID >= 0 then
7615         begin
7616           // check mainunit filename
7617           Ext := ExtractFileExt(Project1.MainUnitInfo.Filename);
7618           if Ext = '' then Ext := '.pas';
7619           if UseMainSourceFile then
7620             NewProgramFN := ExtractFileName(AFilename)
7621           else
7622             NewProgramFN := NewProgramName + Ext;
7623           NewProgramFN := ExtractFilePath(NewLPIFilename) + NewProgramFN;
7624           if (CompareFilenames(NewLPIFilename, NewProgramFN) = 0) then
7625           begin
7626             ACaption:=lisChooseADifferentName;
7627             AText:=Format(lisTheProjectInfoFileIsEqualToTheProjectMainSource,[NewLPIFilename,LineEnding]);
7628             Result:=IDEMessageDialog(ACaption, AText, mtError, [mbAbort,mbRetry]);
7629             if Result=mrAbort then exit;
7630             continue; // try again
7631           end;
7632           // check programname
7633           //if FilenameIsPascalUnit(NewProgramFN)
7634           if (Project1.IndexOfUnitWithName(NewProgramName,true,
7635                                            Project1.MainUnitInfo)>=0) then
7636           begin
7637             ACaption:=lisUnitIdentifierExists;
7638             AText:=Format(lisThereIsAUnitWithTheNameInTheProjectPleaseChoose,[NewProgramName,LineEnding]);
7639             Result:=IDEMessageDialog(ACaption,AText,mtError,[mbRetry,mbAbort]);
7640             if Result=mrAbort then exit;
7641             continue; // try again
7642           end;
7643           Result:=mrOk;
7644         end else begin
7645           NewProgramFN:='';
7646           Result:=mrOk;
7647         end;
7648       until Result<>mrRetry;
7649     finally
7650       InputHistories.StoreFileDialogSettings(SaveDialog);
7651       SaveDialog.Free;
7652     end;
7653 
7654     //DebugLn(['ShowSaveProjectAsDialog NewLPI=',NewLPIFilename,' NewProgramName=',NewProgramName,' NewMainSource=',NewProgramFN]);
7655 
7656     // check if info file or source file already exists
7657     // Note: if user confirms overwriting .lpi do not ask for overwriting .lpr
7658     if FileExistsUTF8(NewLPIFilename) then
7659     begin
7660       if IDESaveDialogClass.NeedOverwritePrompt then
7661       begin
7662         ACaption:=lisOverwriteFile;
7663         AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewLPIFilename, LineEnding]);
7664         Result:=IDEMessageDialog(ACaption, AText, mtConfirmation, [mbOk, mbCancel]);
7665         if Result=mrCancel then exit;
7666       end;
7667     end
7668     else
7669     begin
7670       if FileExistsUTF8(NewProgramFN) then
7671       begin
7672         ACaption:=lisOverwriteFile;
7673         AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewProgramFN, LineEnding]);
7674         Result:=IDEMessageDialog(ACaption, AText, mtConfirmation,[mbOk,mbCancel]);
7675         if Result=mrCancel then exit;
7676       end;
7677     end;
7678 
7679     TitleWasDefault := Project1.TitleIsDefault(true);
7680 
7681     // set new project target filename
7682     if (Project1.TargetFilename<>'')
7683     and ( (CompareText(ExtractFileNameOnly(Project1.TargetFilename),
7684                        ExtractFileNameOnly(Project1.ProjectInfoFile))=0)
7685         or (Project1.ProjectInfoFile='') ) then
7686     begin
7687       // target file is default => change to all build modes, but keep sub directories
7688       // And keep old extension.
7689       NewTargetFN:=ExtractFilePath(Project1.TargetFilename)+ExtractFileNameOnly(NewProgramFN)
7690         +ExtractFileExt(Project1.TargetFilename);
7691       for i := 0 to Project1.BuildModes.Count-1 do
7692         Project1.BuildModes[i].CompilerOptions.TargetFilename:=NewTargetFN;
7693       //DebugLn(['ShowSaveProjectAsDialog changed targetfilename to ',Project1.TargetFilename]);
7694     end;
7695 
7696     // set new project filename
7697     Project1.ProjectInfoFile:=NewLPIFilename;
7698     EnvironmentOptions.AddToRecentProjectFiles(NewLPIFilename);
7699     MainIDE.SetRecentProjectFilesMenu;
7700 
7701     // change main source
7702     if (Project1.MainUnitID >= 0) then
7703     begin
7704       GetMainUnit(MainUnitInfo, MainUnitSrcEdit, true);
7705       if not Project1.ProjResources.RenameDirectives(MainUnitInfo.Filename,NewProgramFN)
7706       then begin
7707         DebugLn(['ShowSaveProjectAsDialog failed renaming directives Old="',MainUnitInfo.Filename,'" New="',NewProgramFN,'"']);
7708         // silently ignore
7709       end;
7710 
7711       // Save old source code, to prevent overwriting it,
7712       // if the file name didn't actually change.
7713       OldBuf := MainUnitInfo.Source;
7714       OldSourceCode := OldBuf.Source;
7715 
7716       // switch MainUnitInfo.Source to new code
7717       NewBuf := CodeToolBoss.CreateFile(NewProgramFN);
7718       if NewBuf=nil then begin
7719         Result:=IDEMessageDialog(lisErrorCreatingFile,
7720           Format(lisUnableToCreateFile3, [LineEnding, NewProgramFN]),
7721           mtError, [mbCancel]);
7722         exit;
7723       end;
7724 
7725       // copy the source to the new buffer
7726       NewBuf.Source:=OldSourceCode;
7727       if (OldBuf.DiskEncoding<>'') and (OldBuf.DiskEncoding<>EncodingUTF8) then
7728       begin
7729         NewBuf.DiskEncoding:=OldBuf.DiskEncoding;
7730         InputHistories.FileEncodings[NewProgramFN]:=NewBuf.DiskEncoding;
7731       end else
7732         InputHistories.FileEncodings[NewProgramFN]:='';
7733 
7734       // assign the new buffer to the MainUnit
7735       MainUnitInfo.Source:=NewBuf;
7736       if MainUnitSrcEdit<>nil then
7737         MainUnitSrcEdit.CodeBuffer:=NewBuf;
7738 
7739       // change program name
7740       MainUnitInfo.Unit_Name:=NewProgramName;
7741       MainUnitInfo.Modified:=true;
7742 
7743       // update source notebook page names
7744       UpdateSourceNames;
7745     end;
7746 
7747     // update paths
7748     prDir := Project1.Directory;
7749     with Project1.CompilerOptions do begin
7750       OtherUnitFiles:=RebaseSearchPath(OtherUnitFiles,OldProjectDir,prDir,true);
7751       IncludePath   :=RebaseSearchPath(IncludePath,OldProjectDir,prDir,true);
7752       Libraries     :=RebaseSearchPath(Libraries,OldProjectDir,prDir,true);
7753       ObjectPath    :=RebaseSearchPath(ObjectPath,OldProjectDir,prDir,true);
7754       SrcPath       :=RebaseSearchPath(SrcPath,OldProjectDir,prDir,true);
7755       DebugPath     :=RebaseSearchPath(DebugPath,OldProjectDir,prDir,true);
7756     end;
7757     // change title
7758     if TitleWasDefault then begin
7759       Project1.Title:=Project1.GetDefaultTitle;
7760       // title does not need to be removed from source, because it was default
7761     end;
7762 
7763     // invalidate cached substituted macros
7764     IncreaseCompilerParseStamp;
7765   finally
7766     Project1.EndUpdate;
7767   end;
7768   Result:=mrOk;
7769   //DebugLn(['ShowSaveProjectAsDialog END OK']);
7770 end;
7771 
7772 function AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult;
7773 var
7774   DataModified: Boolean;
7775   SrcModified: Boolean;
7776 begin
7777   if Project1=nil then exit(mrOk);
7778   if not SomethingOfProjectIsModified then exit(mrOk);
7779 
7780   DataModified:=Project1.SomeDataModified(false);
7781   SrcModified:=SourceEditorManager.SomethingModified(false);
7782 
7783   if Project1.IsVirtual
7784   and (not DataModified)
7785   and (not SrcModified) then begin
7786     // only session changed of a new project => ignore
7787     exit(mrOk)
7788   end;
7789 
7790   if (Project1.SessionStorage=pssInProjectInfo)
7791   or DataModified
7792   then begin
7793     // lpi file will change => ask
7794     Result:=IDEQuestionDialog(lisProjectChanged,
7795         Format(lisSaveChangesToProject, [Project1.GetTitleOrName]),
7796         mtConfirmation, [mrYes,
7797                          mrNoToAll, rsmbNo,
7798                          mrCancel], '');
7799     if Result=mrNoToAll then exit(mrOk);
7800     if Result<>mrYes then exit(mrCancel);
7801   end
7802   else if SrcModified then
7803   begin
7804     // some non project files were changes in the source editor
7805     Result:=IDEQuestionDialog(lisSaveChangedFiles,lisSaveChangedFiles,
7806         mtConfirmation, [mrYes,
7807                          mrNoToAll, rsmbNo,
7808                          mrCancel], '');
7809     if Result=mrNoToAll then exit(mrOk);
7810     if Result<>mrYes then exit(mrCancel);
7811   end
7812   else begin
7813     // only session data changed
7814     if Project1.SessionStorage=pssNone then
7815       // session is not saved => skip
7816       exit(mrOk)
7817     else if not SomethingOfProjectIsModified then
7818       // no change
7819       exit(mrOk)
7820     else begin
7821       // session is saved separately
7822       if EnvironmentOptions.AskSaveSessionOnly then begin
7823         Result:=IDEQuestionDialog(lisProjectSessionChanged,
7824             Format(lisSaveSessionChangesToProject, [Project1.GetTitleOrName]),
7825             mtConfirmation, [mrYes,
7826                              mrNoToAll, rsmbNo,
7827                              mrCancel], '');
7828         if Result=mrNoToAll then exit(mrOk);
7829         if Result<>mrYes then exit(mrCancel);
7830       end;
7831     end;
7832   end;
7833   Result:=SaveProject([sfCanAbort]);
7834   if Result=mrAbort then exit;
7835   if Result<>mrOk then begin
7836     Result:=IDEQuestionDialog(lisChangesWereNotSaved, ContinueText,
7837       mtConfirmation, [mrOk, ContinueBtn, mrAbort]);
7838     if Result<>mrOk then exit(mrCancel);
7839   end;
7840 end;
7841 
7842 function SaveEditorChangesToCodeCache(AEditor: TSourceEditorInterface): boolean;
7843 // save all open sources to code tools cache
7844 
7845   procedure SaveChanges(SaveEditor: TSourceEditorInterface);
7846   var
7847     AnUnitInfo: TUnitInfo;
7848   begin
7849     AnUnitInfo := Project1.UnitWithEditorComponent(SaveEditor);
7850     if (AnUnitInfo<>nil) then
7851     begin
7852       //debugln(['SaveChanges ',AnUnitInfo.Filename,' ',SaveEditor.NeedsUpdateCodeBuffer]);
7853       if SaveEditor.NeedsUpdateCodeBuffer then
7854       begin
7855         SaveEditorChangesToCodeCache:=true;
7856         SaveEditor.UpdateCodeBuffer;
7857         //debugln(['SaveEditorChangesToCodeCache.SaveChanges ',AnUnitInfo.Filename,' Step=',TCodeBuffer(SaveEditor.CodeToolsBuffer).ChangeStep]);
7858       end;
7859     end;
7860   end;
7861 
7862 var
7863   i: integer;
7864 begin
7865   Result:=false;
7866   //debugln(['SaveEditorChangesToCodeCache ']);
7867   if AEditor = nil then begin
7868     for i:=0 to SourceEditorManager.SourceEditorCount - 1 do
7869       SaveChanges(SourceEditorManager.SourceEditors[i]);
7870   end else begin
7871     SaveChanges(AEditor);
7872   end;
7873 end;
7874 
7875 function ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string;
7876   IgnoreErrors, Quiet, Confirm: boolean): TModalResult;
7877 // Replaces all references to a unit
7878 var
7879   OwnerList: TFPList;
7880   ExtraFiles: TStrings;
7881   Files: TStringList;
7882   OldCode: TCodeBuffer;
7883   OldCodeCreated: Boolean;
7884   PascalReferences: TAVLTree;
7885   i: Integer;
7886   MsgResult: TModalResult;
7887   OnlyEditorFiles: Boolean;
7888   aFilename: String;
7889 begin
7890   // compare unitnames case sensitive, maybe only the case changed
7891   if (CompareFilenames(OldFilename,NewFilename)=0) and (OldUnitName=NewUnitName) then
7892     exit(mrOk);
7893   // this was a new file, files on disk can not refer to it
7894   OnlyEditorFiles:=not FilenameIsAbsolute(OldFilename);
7895 
7896   OwnerList:=nil;
7897   OldCode:=nil;
7898   OldCodeCreated:=false;
7899   PascalReferences:=nil;
7900   Files:=TStringList.Create;
7901   try
7902     if OnlyEditorFiles then begin
7903       // search only in open files
7904       for i:=0 to SourceEditorManagerIntf.UniqueSourceEditorCount-1 do begin
7905         aFilename:=SourceEditorManagerIntf.UniqueSourceEditors[i].FileName;
7906         if not FilenameIsPascalSource(aFilename) then continue;
7907         Files.Add(aFileName);
7908       end;
7909       // add project's main source file
7910       if (Project1<>nil) and (Project1.MainUnitID>=0) then
7911         Files.Add(Project1.MainFilename);
7912     end else begin
7913       // get owners of unit
7914       OwnerList:=PkgBoss.GetOwnersOfUnit(NewFilename);
7915       if OwnerList=nil then exit(mrOk);
7916       PkgBoss.ExtendOwnerListWithUsedByOwners(OwnerList);
7917       ReverseList(OwnerList);
7918 
7919       // get source files of packages and projects
7920       ExtraFiles:=PkgBoss.GetSourceFilesOfOwners(OwnerList);
7921       try
7922         if ExtraFiles<>nil then
7923           Files.AddStrings(ExtraFiles);
7924       finally
7925         ExtraFiles.Free;
7926       end;
7927     end;
7928     for i:=Files.Count-1 downto 0 do begin
7929       if (CompareFilenames(Files[i],OldFilename)=0)
7930       or (CompareFilenames(Files[i],NewFilename)=0) then
7931         Files.Delete(i);
7932     end;
7933     //DebugLn(['ReplaceUnitUse ',Files.Text]);
7934 
7935     // commit source editor to codetools
7936     SaveEditorChangesToCodeCache(nil);
7937 
7938     // load or create old unit
7939     OldCode:=CodeToolBoss.LoadFile(OldFilename,true,false);
7940     if OldCode=nil then begin
7941       // create old file in memory so that unit search can find it
7942       OldCode:=CodeToolBoss.CreateFile(OldFilename);
7943       OldCodeCreated:=true;
7944     end;
7945 
7946     // search pascal source references
7947     Result:=GatherUnitReferences(Files,OldCode,false,IgnoreErrors,true,PascalReferences);
7948     if (not IgnoreErrors) and (not Quiet) and (CodeToolBoss.ErrorMessage<>'') then
7949       MainIDE.DoJumpToCodeToolBossError;
7950     if Result<>mrOk then begin
7951       debugln('ReplaceUnitUse GatherUnitReferences failed');
7952       exit;
7953     end;
7954 
7955     // replace
7956     if (PascalReferences<>nil) and (PascalReferences.Count>0) then begin
7957       if Confirm then begin
7958         MsgResult:=IDEQuestionDialog(lisUpdateReferences,
7959           Format(lisTheUnitIsUsedByOtherFilesUpdateReferencesAutomatic,
7960                  [OldUnitName, LineEnding]),
7961           mtConfirmation, [mrYes,mrNo,mrYesToAll,mrNoToAll],'');
7962         case MsgResult of
7963         mrYes: ;
7964         mrYesToAll: EnvironmentOptions.UnitRenameReferencesAction:=urraAlways;
7965         mrNoToAll:
7966           begin
7967             EnvironmentOptions.UnitRenameReferencesAction:=urraNever;
7968             Result:=mrOk;
7969             exit;
7970           end;
7971         else
7972           Result:=mrOk;
7973           exit;
7974         end;
7975       end;
7976       if not CodeToolBoss.RenameIdentifier(PascalReferences,
7977         OldUnitName,NewUnitName)
7978       then begin
7979         if (not IgnoreErrors) and (not Quiet) then
7980           MainIDE.DoJumpToCodeToolBossError;
7981         debugln('ReplaceUnitUse unable to commit');
7982         if not IgnoreErrors then begin
7983           Result:=mrCancel;
7984           exit;
7985         end;
7986       end;
7987     end;
7988 
7989   finally
7990     if OldCodeCreated then
7991       OldCode.IsDeleted:=true;
7992     CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences);
7993     OwnerList.Free;
7994     Files.Free;
7995   end;
7996   //PkgBoss.GetOwnersOfUnit(NewFilename);
7997   Result:=mrOk;
7998 end;
7999 
8000 function DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean;
8001 var
8002   ActiveSourceEditor: TSourceEditor;
8003   ActiveUnitInfo: TUnitInfo;
8004 begin
8005   Assert(Assigned(aLookupRoot),'DesignerUnitIsVirtual: aLookupRoot is not assigned');
8006   MainIDE.GetUnitWithPersistent(aLookupRoot, ActiveSourceEditor, ActiveUnitInfo);
8007   Result := ActiveUnitInfo.IsVirtual;
8008 end;
8009 
8010 end.
8011 
8012