{ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** Abstract: Class TLazSourceFileManager has methods to deal with source files. It deals with projects, packages and the IDE features when needed. The code is copied and refactored from the huge main.pp. The goal was to not call methods defined in TMainIDE but there are still some calls doing it. } unit SourceFileManager; {$mode objfpc}{$H+} interface uses AVL_Tree, typinfo, math, Classes, SysUtils, Controls, Forms, Dialogs, LCLIntf, LCLType, LCLProc, FileProcs, FileUtil, IDEProcs, DialogProcs, IDEDialogs, LConvEncoding, LResources, PropEdits, DefineTemplates, IDEMsgIntf, IDEProtocol, LazarusIDEStrConsts, NewDialog, NewProjectDlg, LazIDEIntf, MainBase, MainBar, MainIntf, MenuIntf, NewItemIntf, ProjectIntf, Project, ProjectDefs, ProjectInspector, CompilerOptions, BasePkgManager, PackageIntf, PackageDefs, PackageSystem, SrcEditorIntf, IDEWindowIntf, SourceEditor, EditorOptions, CustomFormEditor, FormEditor, EmptyMethodsDlg, BaseDebugManager, ControlSelection, TransferMacros, EnvironmentOpts, BuildManager, Designer, EditorMacroListViewer, KeywordFuncLists, FindRenameIdentifier, MsgView, InputHistory, CheckLFMDlg, LCLMemManager, CodeToolManager, CodeToolsStructs, ConvCodeTool, CodeCache; type { TLazSourceFileManager } TLazSourceFileManager = class private function CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult; function FileExistsInIDE(const Filename: string; SearchFlags: TProjectFileSearchFlags): boolean; public constructor Create; destructor Destroy; override; procedure AddRecentProjectFileToEnvironment(const AFilename: string); procedure UpdateSourceNames; function CheckEditorNeedsSave(AEditor: TSourceEditorInterface; IgnoreSharedEdits: Boolean): Boolean; procedure ArrangeSourceEditorAndMessageView(PutOnTop: boolean); // files/units/projects function SomethingOfProjectIsModified(Verbose: boolean = false): boolean; function NewFile(NewFileDescriptor: TProjectFileDescriptor; var NewFilename: string; NewSource: string; NewFlags: TNewFlags; NewOwner: TObject): TModalResult; function NewOther: TModalResult; function NewUnitOrForm(Template: TNewIDEItemTemplate; DefaultDesc: TProjectFileDescriptor): TModalResult; procedure CreateFileDialogFilterForSourceEditorFiles(Filter: string; out AllEditorMask, AllMask: string); function SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult; function CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags):TModalResult; function CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult; function OpenEditorFile(AFileName:string; PageIndex, WindowIndex: integer; AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags): TModalResult; function OpenFileAtCursor(ActiveSrcEdit: TSourceEditor; ActiveUnitInfo: TUnitInfo): TModalResult; function InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult; function InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult; procedure NewProjectFromFile; function CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult; function InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult; function SaveProject(Flags: TSaveFlags): TModalResult; function SaveProjectIfChanged: TModalResult; function CloseProject: TModalResult; procedure OpenProject(aMenuItem: TIDEMenuItem); procedure CloseAll; // methods for 'new unit' function CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor; NewOwner: TObject; NewFilename: string; var NewCodeBuffer: TCodeBuffer; var NewUnitName: string): TModalResult; function CreateNewForm(NewUnitInfo: TUnitInfo; AncestorType: TPersistentClass; ResourceCode: TCodeBuffer; UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult; function NewUniqueComponentName(Prefix: string): string; // methods for 'save unit' function ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult; function SaveUnitComponent(AnUnitInfo: TUnitInfo; LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult; function RemoveLooseEvents(AnUnitInfo: TUnitInfo; OkOnCodeErrors: boolean): TModalResult; function RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string; var LFMCode, LRSCode: TCodeBuffer): TModalResult; // methods for 'open unit' and 'open main unit' private function OpenNotExistingFile(const AFileName:string; Flags: TOpenFlags): TModalResult; function OpenUnknownFile(const AFileName:string; Flags: TOpenFlags; var NewUnitInfo: TUnitInfo; var Handled: boolean): TModalResult; function LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer; IgnoreSourceErrors, AutoCreateResourceCode, ShowAbort: boolean): TModalResult; function FindBaseComponentClass(const AComponentClassName, DescendantClassName: string; out AComponentClass: TComponentClass): boolean; function LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo; const DescendantClassName: string; OpenFlags: TOpenFlags; out AncestorClass: TComponentClass; out AncestorUnitInfo: TUnitInfo): TModalResult; function LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo; const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean; var AComponentClass: TComponentClass; var ComponentUnitInfo: TUnitInfo): TModalResult; function LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer; const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult; public function OpenMainUnit(PageIndex, WindowIndex: integer; Flags: TOpenFlags): TModalResult; function RevertMainUnit: TModalResult; function CheckLFMInEditor(LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult; function OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo; PageIndex, WindowIndex: integer; Flags: TOpenFlags): TModalResult; function LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult; function LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult; function OpenComponent(const UnitFilename: string; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags; out Component: TComponent): TModalResult; // methods for 'close unit' function CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult; function CloseDependingUnitComponents(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult; function UnitComponentIsUsed(AnUnitInfo: TUnitInfo; CheckHasDesigner: boolean): boolean; function RemoveFilesFromProject(AProject: TProject; UnitInfos: TFPList): TModalResult; procedure InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook); // methods for open project, create project from source function CompleteLoadingProjectInfo: TModalResult; // methods for 'save project' private function ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult; function SaveProjectInfo(var Flags: TSaveFlags): TModalResult; procedure GetMainUnit(var MainUnitInfo: TUnitInfo; var MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean); procedure SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo); procedure SaveSourceEditorProjectSpecificSettings; procedure UpdateProjectResourceInfo; public function AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult; function SaveSourceEditorChangesToCodeCache(AEditor: TSourceEditorInterface): boolean; function ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string; IgnoreErrors, Quiet, Confirm: boolean): TModalResult; // related to Designer function DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean; end; function SourceFileMgr: TLazSourceFileManager; function CreateSrcEditPageName(const AnUnitName, AFilename: string; IgnoreEditor: TSourceEditor): string; procedure UpdateDefaultPasFileExt; implementation var SourceFileMgrSingleton: TLazSourceFileManager = nil; function SourceFileMgr: TLazSourceFileManager; // Return always the same instance of SourceFileManager. Create at the first time. begin if SourceFileMgrSingleton = nil then SourceFileMgrSingleton := TLazSourceFileManager.Create; Result := SourceFileMgrSingleton; end; function CreateSrcEditPageName(const AnUnitName, AFilename: string; IgnoreEditor: TSourceEditor): string; begin Result:=AnUnitName; if Result='' then Result:=AFilename; if FilenameIsPascalUnit(Result) then Result:=ExtractFileNameOnly(Result) else Result:=ExtractFileName(Result); Result:=SourceEditorManager.FindUniquePageName(Result,IgnoreEditor); end; procedure UpdateDefaultPasFileExt; var DefPasExt: string; begin // change default pascal file extensions DefPasExt:=PascalExtension[EnvironmentOptions.PascalFileExtension]; if LazProjectFileDescriptors<>nil then LazProjectFileDescriptors.DefaultPascalFileExt:=DefPasExt; end; { TLazSourceFileManager } constructor TLazSourceFileManager.Create; begin end; destructor TLazSourceFileManager.Destroy; begin inherited Destroy; end; function TLazSourceFileManager.CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult; var MainUnitInfo: TUnitInfo; MainUsesSection,ImplementationUsesSection: TStrings; MsgResult: TModalResult; begin Result:=mrOk; if (Project1=nil) then exit; if Project1.SkipCheckLCLInterfaces then exit; MainUnitInfo:=Project1.MainUnitInfo; if (MainUnitInfo=nil) or (MainUnitInfo.Source=nil) then exit; if PackageGraph.FindDependencyRecursively(Project1.FirstRequiredDependency, PackageGraph.LCLBasePackage)=nil then exit; // project does not use LCLBase // project uses LCLBase MainUsesSection:=nil; ImplementationUsesSection:=nil; try if not CodeToolBoss.FindUsedUnitNames(MainUnitInfo.Source, MainUsesSection,ImplementationUsesSection) then exit; if (UTF8SearchInStringList(MainUsesSection,'forms')<0) and (UTF8SearchInStringList(ImplementationUsesSection,'forms')<0) then exit; // project uses lcl unit Forms if (UTF8SearchInStringList(MainUsesSection,'interfaces')>=0) or (UTF8SearchInStringList(ImplementationUsesSection,'interfaces')>=0) then exit; // project uses lcl unit Forms, but not unit interfaces // this will result in strange linker error if not Silent then begin MsgResult:=IDEQuestionDialog(lisCCOWarningCaption, Format(lisTheProjectDoesNotUseTheLCLUnitInterfacesButItSeems, [LineEnding]), mtWarning, [mrYes, lisAddUnitInterfaces, mrNo, dlgIgnoreVerb, mrNoToAll, lisAlwaysIgnore, mrCancel]); case MsgResult of mrNo: exit; mrNoToAll: begin Project1.SkipCheckLCLInterfaces:=true; exit; end; mrCancel: exit(mrCancel); end; end; CodeToolBoss.AddUnitToMainUsesSection(MainUnitInfo.Source,'Interfaces',''); finally MainUsesSection.Free; ImplementationUsesSection.Free; end; end; procedure TLazSourceFileManager.AddRecentProjectFileToEnvironment(const AFilename: string); begin EnvironmentOptions.AddToRecentProjectFiles(AFilename); MainIDE.SetRecentProjectFilesMenu; MainIDE.SaveEnvironment; end; procedure TLazSourceFileManager.UpdateSourceNames; var i: integer; AnUnitInfo: TUnitInfo; SourceName, PageName: string; AEditor: TSourceEditor; begin for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin AEditor := SourceEditorManager.SourceEditors[i]; AnUnitInfo := Project1.UnitWithEditorComponent(AEditor); if AnUnitInfo=nil then continue; if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin SourceName:=CodeToolBoss.GetCachedSourceName(AnUnitInfo.Source); if SourceName<>'' then AnUnitInfo.ReadUnitNameFromSource(true); end else SourceName:=''; PageName:=CreateSrcEditPageName(SourceName, AnUnitInfo.Filename, AEditor); AEditor.PageName := PageName; end; end; function TLazSourceFileManager.CheckEditorNeedsSave(AEditor: TSourceEditorInterface; IgnoreSharedEdits: Boolean): Boolean; var AnEditorInfo: TUnitEditorInfo; AnUnitInfo: TUnitInfo; begin Result := False; if AEditor = nil then exit; AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor); if AnEditorInfo = nil then exit; AnUnitInfo := AnEditorInfo.UnitInfo; if (AnUnitInfo.OpenEditorInfoCount > 1) and IgnoreSharedEdits then exit; // save some meta data of the source SaveSrcEditorProjectSpecificSettings(AnEditorInfo); Result := (AEditor.Modified) or (AnUnitInfo.Modified); end; procedure TLazSourceFileManager.ArrangeSourceEditorAndMessageView(PutOnTop: boolean); var SrcNoteBook: TSourceNotebook; Layout: TSimpleWindowLayout; begin MainIDE.DoShowMessagesView(PutOnTop); if SourceEditorManager.SourceWindowCount = 0 then exit; SrcNoteBook := SourceEditorManager.SourceWindows[0]; Layout:=IDEWindowCreators.SimpleLayoutStorage.ItemByFormID(SrcNoteBook.Name); if (Layout<>nil) and (Layout.WindowPlacement=iwpDefault) and ((SrcNoteBook.Top + SrcNoteBook.Height) > MessagesView.Top) and (MessagesView.Parent = nil) then SrcNoteBook.Height := Max(50,Min(SrcNoteBook.Height, MessagesView.Top-SrcNoteBook.Top)); if PutOnTop then begin IDEWindowCreators.ShowForm(MessagesView,true); SourceEditorManager.ShowActiveWindowOnTop(False); end; end; function TLazSourceFileManager.SomethingOfProjectIsModified(Verbose: boolean): boolean; begin Result:=(Project1<>nil) and (Project1.SomethingModified(true,true,Verbose) or SourceEditorManager.SomethingModified(Verbose)); end; function TLazSourceFileManager.FileExistsInIDE(const Filename: string; SearchFlags: TProjectFileSearchFlags): boolean; begin Result:=FileExistsCached(Filename) or ((Project1<>nil) and (Project1.UnitInfoWithFilename(Filename,SearchFlags)<>nil)); end; function TLazSourceFileManager.NewFile(NewFileDescriptor: TProjectFileDescriptor; var NewFilename: string; NewSource: string; NewFlags: TNewFlags; NewOwner: TObject): TModalResult; function BeautifySrc(const s: string): string; begin Result:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(s,0); end; var NewUnitInfo:TUnitInfo; NewSrcEdit: TSourceEditor; NewUnitName: string; NewBuffer: TCodeBuffer; OldUnitIndex: Integer; AncestorType: TPersistentClass; LFMFilename: String; SearchFlags: TProjectFileSearchFlags; LFMSourceText: String; LFMCode: TCodeBuffer; AProject: TProject; LRSFilename: String; ResType: TResourceType; SrcNoteBook: TSourceNotebook; AShareEditor: TSourceEditor; DisableAutoSize: Boolean; APackage: TLazPackage; IsPartOfProject: Boolean; RequiredPackages: String; begin //debugln('TLazSourceFileManager.NewEditorFile A NewFilename=',NewFilename); // empty NewFilename is ok, it will be auto generated SaveSourceEditorChangesToCodeCache(nil); // convert macros in filename if nfConvertMacros in NewFlags then begin if not GlobalMacroList.SubstituteStr(NewFilename) then begin Result:=mrCancel; exit; end; end; Result:=NewFileDescriptor.Init(NewFilename,NewOwner,NewSource,nfQuiet in NewFlags); if Result<>mrOk then exit; if FilenameIsAbsolute(NewFilename) and DirectoryExistsUTF8(NewFilename) then begin IDEMessageDialog(lisFileIsDirectory, lisUnableToCreateNewFileBecauseThereIsAlreadyADirecto, mtError,[mbCancel]); exit(mrCancel); end; if NewOwner is TProject then AProject:=TProject(NewOwner) else AProject:=Project1; if NewOwner is TLazPackage then APackage:=TLazPackage(NewOwner) else APackage:=nil; OldUnitIndex:=AProject.IndexOfFilename(NewFilename); if OldUnitIndex>=0 then begin // the file is not really new // => close form Result:=CloseUnitComponent(AProject.Units[OldUnitIndex], [cfCloseDependencies,cfSaveDependencies]); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile CloseUnitComponent failed']); exit; end; end; IsPartOfProject:=(nfIsPartOfProject in NewFlags) or (NewOwner is TProject) or (AProject.FileIsInProjectDir(NewFilename) and (not (nfIsNotPartOfProject in NewFlags))); // add required packages //debugln(['TLazSourceFileManager.NewFile NewFileDescriptor.RequiredPackages="',NewFileDescriptor.RequiredPackages,'" ',DbgSName(NewFileDescriptor)]); RequiredPackages:=NewFileDescriptor.RequiredPackages; if (RequiredPackages='') and (NewFileDescriptor.ResourceClass<>nil) then begin if (NewFileDescriptor.ResourceClass.InheritsFrom(TForm)) or (NewFileDescriptor.ResourceClass.InheritsFrom(TFrame)) then RequiredPackages:='LCL'; end; if RequiredPackages<>'' then begin if IsPartOfProject then begin Result:=PkgBoss.AddProjectDependencies(Project1,RequiredPackages); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile PkgBoss.AddProjectDependencies failed RequiredPackages="',RequiredPackages,'"']); exit; end; end; if APackage<>nil then begin Result:=PkgBoss.AddPackageDependency(APackage,RequiredPackages); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile PkgBoss.AddPackageDependency failed RequiredPackages="',RequiredPackages,'"']); exit; end; end; end; // check if the new file fits Result:=NewFileDescriptor.CheckOwner(nfQuiet in NewFlags); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile NewFileDescriptor.CheckOwner failed NewFilename="',NewFilename,'"']); exit; end; // create new codebuffer and apply naming conventions NewBuffer:=nil; NewUnitName:=''; Result:=CreateNewCodeBuffer(NewFileDescriptor,NewOwner,NewFilename,NewBuffer,NewUnitName); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile CreateNewCodeBuffer failed NewFilename="',NewFilename,'"']); exit; end; NewFilename:=NewBuffer.Filename; if OldUnitIndex>=0 then begin // the file is not really new NewUnitInfo:=AProject.Units[OldUnitIndex]; // assign source NewUnitInfo.Source:=NewBuffer; end else NewUnitInfo:=TUnitInfo.Create(NewBuffer); //debugln(['TLazSourceFileManager.NewFile ',NewUnitInfo.Filename,' ',NewFilename]); NewUnitInfo.ImproveUnitNameCache(NewUnitName); NewUnitInfo.BuildFileIfActive:=NewFileDescriptor.BuildFileIfActive; NewUnitInfo.RunFileIfActive:=NewFileDescriptor.RunFileIfActive; // create source code //debugln('TLazSourceFileManager.NewEditorFile A nfCreateDefaultSrc=',nfCreateDefaultSrc in NewFlags,' ResourceClass=',dbgs(NewFileDescriptor.ResourceClass)); if nfCreateDefaultSrc in NewFlags then begin if (NewFileDescriptor.ResourceClass<>nil) then begin NewUnitInfo.ComponentName:=NewUniqueComponentName(NewFileDescriptor.DefaultResourceName); NewUnitInfo.ComponentResourceName:=''; end; NewUnitInfo.CreateStartCode(NewFileDescriptor,NewUnitName); end else begin if nfBeautifySrc in NewFlags then NewBuffer.Source:=BeautifySrc(NewSource) else NewBuffer.Source:=NewSource; NewUnitInfo.Modified:=true; end; // add to project NewUnitInfo.Loaded:=true; NewUnitInfo.IsPartOfProject:=IsPartOfProject; if OldUnitIndex<0 then begin AProject.AddFile(NewUnitInfo, NewFileDescriptor.AddToProject and NewFileDescriptor.IsPascalUnit and NewUnitInfo.IsPartOfProject and (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags)); end; // syntax highlighter type NewUnitInfo.DefaultSyntaxHighlighter := FilenameToLazSyntaxHighlighter(NewFilename); if nfOpenInEditor in NewFlags then begin // open a new sourceeditor SrcNoteBook := SourceEditorManager.ActiveOrNewSourceWindow; AShareEditor := nil; if NewUnitInfo.OpenEditorInfoCount > 0 then AShareEditor := TSourceEditor(NewUnitInfo.OpenEditorInfo[0].EditorComponent); NewSrcEdit := SrcNoteBook.NewFile( CreateSrcEditPageName(NewUnitInfo.Unit_Name, NewUnitInfo.Filename, AShareEditor), NewUnitInfo.Source, True, AShareEditor); MainIDEBar.itmFileClose.Enabled:=True; MainIDEBar.itmFileCloseAll.Enabled:=True; NewSrcEdit.SyntaxHighlighterType:=NewUnitInfo.EditorInfo[0].SyntaxHighlighter; NewUnitInfo.GetClosedOrNewEditorInfo.EditorComponent := NewSrcEdit; NewSrcEdit.EditorComponent.CaretXY := Point(1,1); // create component AncestorType:=NewFileDescriptor.ResourceClass; //DebugLn(['TLazSourceFileManager.NewFile AncestorType=',dbgsName(AncestorType),' ComponentName',NewUnitInfo.ComponentName]); if AncestorType<>nil then begin ResType:=MainBuildBoss.GetResourceType(NewUnitInfo); LFMSourceText:=NewFileDescriptor.GetResourceSource(NewUnitInfo.ComponentName); //DebugLn(['TLazSourceFileManager.NewFile LFMSourceText=',LFMSourceText]); if LFMSourceText<>'' then begin // the NewFileDescriptor provides a custom .lfm source // -> put it into a new .lfm buffer and load it LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm'); LFMCode:=CodeToolBoss.CreateFile(LFMFilename); LFMCode.Source:=LFMSourceText; //debugln('TLazSourceFileManager.NewEditorFile A ',LFMFilename); Result:=LoadLFM(NewUnitInfo,LFMCode,[],[]); //DebugLn(['TLazSourceFileManager.NewFile ',dbgsName(NewUnitInfo.Component),' ',dbgsName(NewUnitInfo.Component.ClassParent)]); // make sure the .lrs file exists if (ResType=rtLRS) and NewUnitInfo.IsVirtual then begin LRSFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lrs'); CodeToolBoss.CreateFile(LRSFilename); end; if (NewUnitInfo.Component<>nil) and NewFileDescriptor.UseCreateFormStatements and NewUnitInfo.IsPartOfProject and AProject.AutoCreateForms and (pfMainUnitHasCreateFormStatements in AProject.Flags) then begin AProject.AddCreateFormToProjectFile(NewUnitInfo.Component.ClassName, NewUnitInfo.Component.Name); end; end else begin // create a designer form for a form/datamodule/frame //DebugLn(['TLazSourceFileManager.NewFile Name=',NewFileDescriptor.Name,' Class=',NewFileDescriptor.ClassName]); DisableAutoSize:=true; Result := CreateNewForm(NewUnitInfo, AncestorType, nil, NewFileDescriptor.UseCreateFormStatements, DisableAutoSize); if DisableAutoSize and (NewUnitInfo.Component<>nil) and (NewUnitInfo.Component is TControl) then TControl(NewUnitInfo.Component).EnableAutoSizing; end; if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile create designer form failed ',NewUnitInfo.Filename]); exit; end; end; // show form and select form if NewUnitInfo.Component<>nil then begin // show form MainIDE.CreateObjectInspector; MainIDE.DoShowDesignerFormOfCurrentSrc; end else begin MainIDE.DisplayState:= dsSource; end; end else begin // do not open in editor end; // Update HasResources property (if the .lfm file was created separately) if (not NewUnitInfo.HasResources) and FilenameIsPascalUnit(NewUnitInfo.Filename) then begin //debugln('TLazSourceFileManager.NewFile no HasResources ',NewUnitInfo.Filename); LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm'); SearchFlags:=[]; if NewUnitInfo.IsPartOfProject then Include(SearchFlags,pfsfOnlyProjectFiles); if NewUnitInfo.IsVirtual then Include(SearchFlags,pfsfOnlyVirtualFiles); if (AProject.UnitInfoWithFilename(LFMFilename,SearchFlags)<>nil) then begin //debugln('TLazSourceFileManager.NewFile no HasResources ',NewUnitInfo.Filename,' ResourceFile exists'); NewUnitInfo.HasResources:=true; end; end; if (nfAskForFilename in NewFlags) then begin // save and ask for filename NewUnitInfo.Modified:=true; Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles,sfSaveAs]); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile SaveEditorFile failed ',NewFilename]); exit; end; end else if nfSave in NewFlags then begin if (nfOpenInEditor in NewFlags) or NewBuffer.IsVirtual then begin // save and ask for filename if needed NewUnitInfo.Modified:=true; Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles]); if Result<>mrOk then begin debugln(['TLazSourceFileManager.NewFile SaveEditorFile SaveAs failed ',NewFilename]); exit; end; end else begin // save quietly NewBuffer.Save; end; end; Result:=mrOk; //DebugLn('TLazSourceFileManager.NewEditorFile END ',NewUnitInfo.Filename); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.NewUnit end');{$ENDIF} end; function TLazSourceFileManager.NewOther: TModalResult; var NewIDEItem: TNewIDEItemTemplate; NewProjFile: TNewItemProjectFile; begin Result:=ShowNewIDEItemDialog(NewIDEItem); if Result<>mrOk then exit; if NewIDEItem is TNewItemProjectFile then begin // file NewProjFile:=TNewItemProjectFile(NewIDEItem); if NewProjFile.Descriptor<>nil then NewProjFile.Descriptor.Owner:=Project1; try Result:=MainIDE.DoNewEditorFile(NewProjFile.Descriptor, '','',[nfOpenInEditor,nfCreateDefaultSrc]); finally if NewProjFile.Descriptor<>nil then NewProjFile.Descriptor.Owner:=nil; end; end else if NewIDEItem is TNewItemProject then // project Result:=MainIDE.DoNewProject(TNewItemProject(NewIDEItem).Descriptor) else if NewIDEItem is TNewItemPackage then // packages PkgBoss.DoNewPackage else IDEMessageDialog(ueNotImplCap, lisSorryThisTypeIsNotYetImplemented, mtInformation,[mbOk]); end; function TLazSourceFileManager.NewUnitOrForm(Template: TNewIDEItemTemplate; DefaultDesc: TProjectFileDescriptor): TModalResult; var Desc: TProjectFileDescriptor; Flags: TNewFlags; begin if (Template is TNewItemProjectFile) and Template.VisibleInNewDialog then Desc:=TNewItemProjectFile(Template).Descriptor else Desc:=DefaultDesc; Flags:=[nfOpenInEditor,nfCreateDefaultSrc]; if (not Project1.IsVirtual) and EnvironmentOptions.AskForFilenameOnNewFile then Flags:=Flags+[nfAskForFilename,nfSave]; Desc.Owner:=Project1; try Result := MainIDE.DoNewEditorFile(Desc,'','',Flags); finally Desc.Owner:=nil; end; end; procedure TLazSourceFileManager.CreateFileDialogFilterForSourceEditorFiles(Filter: string; out AllEditorMask, AllMask: string); // Filter: a TFileDialog filter, e.g. Pascal|*.pas;*.pp|Text|*.txt // AllEditorExt: a mask for all open files in the source editor, that are not // in Filter, e.g. '*.txt;*.xml' // AllFilter: all masks of Filter and AllEditorExt, e.g. '*.pas;*.pp;*.inc' var i: Integer; SrcEdit: TSourceEditor; Ext: String; begin AllMask:='|'+TFileDialog.ExtractAllFilterMasks(Filter); AllEditorMask:='|'; for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin SrcEdit:=SourceEditorManager.SourceEditors[i]; Ext:=ExtractFileExt(SrcEdit.FileName); if Ext<>'' then begin Ext:='*'+Ext; if (TFileDialog.FindMaskInFilter(AllMask,Ext)>0) or (TFileDialog.FindMaskInFilter(AllEditorMask,Ext)>0) then continue; if AllEditorMask<>'|' then AllEditorMask:=AllEditorMask+';'; AllEditorMask:=AllEditorMask+Ext; end; end; System.Delete(AllMask,1,1); System.Delete(AllEditorMask,1,1); if AllEditorMask<>'' then begin if AllMask<>'' then AllMask:=AllMask+';'; AllMask:=AllMask+AllEditorMask; end; end; function TLazSourceFileManager.SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult; var AnUnitInfo, MainUnitInfo: TUnitInfo; TestFilename, DestFilename: string; LRSCode, LFMCode: TCodeBuffer; OldUnitName, OldFilename: String; NewUnitName, NewFilename: String; WasVirtual, WasPascalSource, CanAbort, Confirm: Boolean; SaveProjectFlags: TSaveFlags; EMacro: TEditorMacro; begin Result:=mrCancel; CanAbort:=[sfCanAbort,sfProjectSaving]*Flags<>[]; //writeln('TLazSourceFileManager.SaveEditorFile A PageIndex=',PageIndex,' Flags=',SaveFlagsToString(Flags)); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.SaveEditorFile A');{$ENDIF} if not (MainIDE.ToolStatus in [itNone,itDebugger]) then exit(mrAbort); if AEditor=nil then exit(mrCancel); AnUnitInfo := Project1.UnitWithEditorComponent(AEditor); if AnUnitInfo=nil then exit(mrCancel); // do not save a unit which is currently reverting if AnUnitInfo.IsReverting then exit(mrOk); WasVirtual:=AnUnitInfo.IsVirtual; WasPascalSource:=FilenameIsPascalSource(AnUnitInfo.Filename); // if this file is part of the project and the project is virtual then save // project first if (not (sfProjectSaving in Flags)) and Project1.IsVirtual and AnUnitInfo.IsPartOfProject then begin SaveProjectFlags:=Flags*[sfSaveToTestDir]; if AnUnitInfo=Project1.MainUnitInfo then Include(SaveProjectFlags,sfSaveMainSourceAs); Result:=SaveProject(SaveProjectFlags); exit; end; // update codetools cache and collect Modified flags if not (sfProjectSaving in Flags) then SaveSourceEditorChangesToCodeCache(nil); if (uifInternalFile in AnUnitInfo.Flags) then begin if (copy(AnUnitInfo.Filename, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive) then begin // save to macros EMacro := MacroListViewer.MacroByFullName(AnUnitInfo.Filename); if EMacro <> nil then begin EMacro.SetFromSource(AEditor.SourceText); if EMacro.IsInvalid and (EMacro.ErrorMsg <> '') then MessagesView.AddMsg(EMacro.ErrorMsg, '', -1); end; MacroListViewer.UpdateDisplay; AnUnitInfo.ClearModifieds; AEditor.Modified:=false; Result := mrOK; exit; end; // unknown internal file => skip exit(mrOk); end; // if this is a new unit then a simple Save becomes a SaveAs if (not (sfSaveToTestDir in Flags)) and (AnUnitInfo.IsVirtual) then Include(Flags,sfSaveAs); // if this is the main source and has the same name as the lpi // rename the project // Note: // Changing the main source file without the .lpi is possible only by // manually editing the lpi file, because this is only needed in // special cases (rare functions don't need front ends). MainUnitInfo:=AnUnitInfo.Project.MainUnitInfo; if (sfSaveAs in Flags) and (not (sfProjectSaving in Flags)) and (AnUnitInfo=MainUnitInfo) then begin Result:=SaveProject([sfSaveAs,sfSaveMainSourceAs]); exit; end; // if nothing modified then a simple Save can be skipped //debugln(['TLazSourceFileManager.SaveEditorFile A ',AnUnitInfo.Filename,' ',AnUnitInfo.NeedsSaveToDisk]); if ([sfSaveToTestDir,sfSaveAs]*Flags=[]) and (not AnUnitInfo.NeedsSaveToDisk) then begin if AEditor.Modified then begin AnUnitInfo.SessionModified:=true; AEditor.Modified:=false; end; exit(mrOk); end; // check if file is writable on disk if (not AnUnitInfo.IsVirtual) and FileExistsUTF8(AnUnitInfo.Filename) then AnUnitInfo.FileReadOnly:=not FileIsWritable(AnUnitInfo.Filename) else AnUnitInfo.FileReadOnly:=false; // if file is readonly then a simple Save is skipped if (AnUnitInfo.ReadOnly) and ([sfSaveToTestDir,sfSaveAs]*Flags=[]) then exit(mrOk); // load old resource file LFMCode:=nil; LRSCode:=nil; if WasPascalSource then begin Result:=LoadResourceFile(AnUnitInfo,LFMCode,LRSCode, not (sfSaveAs in Flags),true,CanAbort); if not (Result in [mrIgnore,mrOk]) then exit; end; OldUnitName:=''; if WasPascalSource then OldUnitName:=AnUnitInfo.ParseUnitNameFromSource(true); OldFilename:=AnUnitInfo.Filename; if [sfSaveAs,sfSaveToTestDir]*Flags=[sfSaveAs] then begin // let user choose a filename NewFilename:=OldFilename; Result:=ShowSaveFileAsDialog(NewFilename,AnUnitInfo,LFMCode,LRSCode,CanAbort); if not (Result in [mrIgnore,mrOk]) then exit; end; // save source // a) do before save events if EditorOpts.AutoRemoveEmptyMethods and (AnUnitInfo.Component<>nil) then begin // Note: When removing published methods, the source, the lfm, the lrs // and the form must be changed. At the moment editing the lfm without // the component is not yet implemented. Result:=RemoveEmptyMethods(AnUnitInfo.Source, AnUnitInfo.Component.ClassName,0,0,false,[pcsPublished]); if Result=mrAbort then exit; end; // b) do actual save if (sfSaveToTestDir in Flags) or AnUnitInfo.IsVirtual then begin // save source to test directory TestFilename := MainBuildBoss.GetTestUnitFilename(AnUnitInfo); if TestFilename <> '' then begin //DebugLn(['TLazSourceFileManager.SaveEditorFile TestFilename="',TestFilename,'" Size=',AnUnitInfo.Source.SourceLength]); Result := AnUnitInfo.WriteUnitSourceToFile(TestFilename); if Result <> mrOk then Exit; DestFilename := TestFilename; end else exit(mrCancel); end else begin if AnUnitInfo.Modified or AnUnitInfo.NeedsSaveToDisk then begin // save source to file Result := AnUnitInfo.WriteUnitSource; if Result <> mrOK then exit; DestFilename := AnUnitInfo.Filename; end; end; if sfCheckAmbiguousFiles in Flags then MainBuildBoss.CheckAmbiguousSources(DestFilename,false); {$IFDEF IDE_DEBUG} writeln('*** HasResources=',AnUnitInfo.HasResources); {$ENDIF} {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.SaveEditorFile B');{$ENDIF} // save resource file and lfm file if (LRSCode<>nil) or (AnUnitInfo.Component<>nil) then begin Result:=SaveUnitComponent(AnUnitInfo,LRSCode,LFMCode,Flags); if not (Result in [mrIgnore, mrOk]) then exit; end; // unset all modified flags if not (sfSaveToTestDir in Flags) then begin AnUnitInfo.ClearModifieds; AEditor.Modified:=false; MainIDE.UpdateSaveMenuItemsAndButtons(not (sfProjectSaving in Flags)); end; TSourceEditor(AEditor).SourceNotebook.UpdateStatusBar; // fix all references NewUnitName:=''; if FilenameIsPascalSource(AnUnitInfo.Filename) then NewUnitName:=AnUnitInfo.ParseUnitNameFromSource(true); NewFilename:=AnUnitInfo.Filename; if (NewUnitName<>'') and ((OldUnitName<>NewUnitName) or (CompareFilenames(OldFilename,NewFilename)<>0)) then begin if EnvironmentOptions.UnitRenameReferencesAction<>urraNever then begin // silently update references of new units (references were auto created // and keeping old references makes no sense) Confirm:=(EnvironmentOptions.UnitRenameReferencesAction=urraAsk) and (not WasVirtual); Result:=ReplaceUnitUse(OldFilename,OldUnitName,NewFilename,NewUnitName, true,true,Confirm); if Result<>mrOk then exit; end; end; {$IFDEF IDE_VERBOSE} debugln(['TLazSourceFileManager.SaveEditorFile END ',NewFilename,' AnUnitInfo.Modified=',AnUnitInfo.Modified,' AEditor.Modified=',AEditor.Modified]); {$ENDIF} Result:=mrOk; end; function TLazSourceFileManager.CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags): TModalResult; var AnUnitInfo: TUnitInfo; ACaption, AText: string; i: integer; AnEditorInfo: TUnitEditorInfo; SrcEditWasFocused: Boolean; SrcEdit: TSourceEditor; begin {$IFDEF IDE_DEBUG} //debugln('TLazSourceFileManager.CloseEditorFile A PageIndex=',IntToStr(AnUnitInfo.PageIndex)); {$ENDIF} Result:=mrCancel; if AEditor = nil then exit; AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor); //AnUnitInfo := Project1.UnitWithEditorComponent(AEditor); if AnEditorInfo = nil then begin // we need to close the page anyway or else we might enter a loop DebugLn('TLazSourceFileManager.CloseEditorFile INCONSISTENCY: NO AnUnitInfo'); SourceEditorManager.CloseFile(AEditor); Result:=mrOk; exit; end; AnUnitInfo := AnEditorInfo.UnitInfo; AnUnitInfo.SessionModified:=true; SrcEditWasFocused:=(AnEditorInfo.EditorComponent<>nil) and (AnEditorInfo.EditorComponent.EditorControl<>nil) and AnEditorInfo.EditorComponent.EditorControl.Focused; //debugln(['TLazSourceFileManager.CloseEditorFile File=',AnUnitInfo.Filename,' WasFocused=',SrcEditWasFocused]); try //debugln(['TLazSourceFileManager.CloseEditorFile File=',AnUnitInfo.Filename,' UnitSession=',AnUnitInfo.SessionModified,' ProjSession=',project1.SessionModified]); if AnUnitInfo.OpenEditorInfoCount > 1 then begin // opened multiple times => close one instance SourceEditorManager.CloseFile(AEditor); Result:=mrOk; exit; end; if (AnUnitInfo.Component<>nil) and (MainIDE.LastFormActivated<>nil) and (TDesigner(MainIDE.LastFormActivated.Designer).LookupRoot=AnUnitInfo.Component) then MainIDE.LastFormActivated:=nil; // save some meta data of the source SaveSrcEditorProjectSpecificSettings(AnEditorInfo); // if SaveFirst then save the source if (cfSaveFirst in Flags) and (not AnUnitInfo.ReadOnly) and ((AEditor.Modified) or (AnUnitInfo.Modified)) then begin if not (cfQuiet in Flags) then begin // ask user if AnUnitInfo.Filename<>'' then AText:=Format(lisFileHasChangedSave, ['"', AnUnitInfo.Filename, '"']) else if AnUnitInfo.Unit_Name<>'' then AText:=Format(lisUnitHasChangedSave, ['"', AnUnitInfo.Unit_name, '"']) else AText:=Format(lisSourceOfPageHasChangedSave, ['"', TSourceEditor(AEditor).PageName, '"']); ACaption:=lisSourceModified; Result:=IDEQuestionDialog(ACaption, AText, mtConfirmation, [mrYes, lisMenuSave, mrNo, lisDiscardChanges, mrAbort ]); end else Result:=mrYes; if Result=mrYes then begin Result:=SaveEditorFile(AnEditorInfo.EditorComponent,[sfCheckAmbiguousFiles]); end; if Result=mrAbort then exit; Result:=mrOk; end; // add to recent file list if (not AnUnitInfo.IsVirtual) and (not (cfProjectClosing in Flags)) then begin EnvironmentOptions.AddToRecentOpenFiles(AnUnitInfo.Filename); MainIDE.SetRecentFilesMenu; end; // close form soft (keep it if used by another component) CloseUnitComponent(AnUnitInfo,[]); // close source editor SourceEditorManager.CloseFile(AnEditorInfo.EditorComponent); MainIDEBar.itmFileClose.Enabled:=SourceEditorManager.SourceEditorCount > 0; MainIDEBar.itmFileCloseAll.Enabled:=MainIDEBar.itmFileClose.Enabled; // free sources, forget changes if (AnUnitInfo.Source<>nil) then begin if (Project1.MainUnitInfo=AnUnitInfo) and (not (cfProjectClosing in Flags)) then begin AnUnitInfo.Source.Revert; end else begin AnUnitInfo.Source.IsDeleted:=true; end; end; // close file in project AnUnitInfo.Loaded:=false; if AnUnitInfo<>Project1.MainUnitInfo then AnUnitInfo.Source:=nil; if not (cfProjectClosing in Flags) then begin i:=Project1.IndexOf(AnUnitInfo); if (i<>Project1.MainUnitID) and AnUnitInfo.IsVirtual then begin Project1.RemoveUnit(i); end; end; finally if SrcEditWasFocused then begin // before closing the syendit was focused. Focus the current synedit. SrcEdit := SourceEditorManager.ActiveEditor; if (SrcEdit<>nil) and (SrcEdit.EditorControl<>nil) and (SrcEdit.EditorControl.CanFocus) then SrcEdit.EditorControl.SetFocus; //debugln(['TLazSourceFileManager.CloseEditorFile Focus=',SrcEdit.FileName,' Editor=',DbgSName(SrcEdit.EditorControl),' Focused=',(SrcEdit.EditorControl<>nil) and (SrcEdit.EditorControl.Focused)]); end; end; {$IFDEF IDE_DEBUG} DebugLn('TLazSourceFileManager.CloseEditorFile end'); {$ENDIF} Result:=mrOk; end; function TLazSourceFileManager.CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult; var UnitIndex: Integer; AnUnitInfo: TUnitInfo; begin Result:=mrOk; if Filename='' then exit; UnitIndex:=Project1.IndexOfFilename(TrimFilename(Filename), [pfsfOnlyEditorFiles]); if UnitIndex<0 then exit; AnUnitInfo:=Project1.Units[UnitIndex]; Result:=mrOk; while (AnUnitInfo.OpenEditorInfoCount > 0) and (Result = mrOK) do Result:=CloseEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, Flags); end; function TLazSourceFileManager.OpenEditorFile(AFileName: string; PageIndex, WindowIndex: integer; AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags): TModalResult; var UnitIndex: integer; UnknownFile, Handled: boolean; NewUnitInfo:TUnitInfo; NewBuf: TCodeBuffer; FilenameNoPath: String; LoadBufferFlags: TLoadBufferFlags; DiskFilename: String; Reverting: Boolean; CanAbort: boolean; NewEditorInfo: TUnitEditorInfo; function OpenResource: TModalResult; var CloseFlags: TCloseFlags; begin // read form data if FilenameIsPascalUnit(AFilename) then begin // this could be a unit with a form //debugln('TLazSourceFileManager.OpenEditorFile ',AFilename,' ',OpenFlagsToString(Flags)); if ([ofDoNotLoadResource]*Flags=[]) and ( (ofDoLoadResource in Flags) or ((ofProjectLoading in Flags) and NewUnitInfo.LoadedDesigner and (not Project1.AutoOpenDesignerFormsDisabled) and EnvironmentOptions.AutoCreateFormsOnOpen)) then begin // -> try to (re)load the lfm file //debugln(['TLazSourceFileManager.OpenEditorFile Loading LFM for ',NewUnitInfo.Filename,' LoadedDesigner=',NewUnitInfo.LoadedDesigner]); CloseFlags:=[cfSaveDependencies]; if ofRevert in Flags then Include(CloseFlags,cfCloseDependencies); Result:=LoadLFM(NewUnitInfo,Flags,CloseFlags); if Result<>mrOk then begin DebugLn(['OpenResource DoLoadLFM failed']); exit; end; end else begin Result:=mrOk; end; end else if NewUnitInfo.Component<>nil then begin // this is no pascal source and there is a designer form // This can be the case, when the file is renamed and/or reverted // -> close form Result:=CloseUnitComponent(NewUnitInfo, [cfCloseDependencies,cfSaveDependencies]); if Result<>mrOk then begin DebugLn(['OpenResource CloseUnitComponent failed']); end; end else begin Result:=mrOk; end; if NewUnitInfo.Component=nil then NewUnitInfo.LoadedDesigner:=false; end; begin {$IFDEF IDE_VERBOSE} DebugLn(''); DebugLn(['*** TLazSourceFileManager.OpenEditorFile START "',AFilename,'" ',OpenFlagsToString(Flags),' Window=',WindowIndex,' Page=',PageIndex]); {$ENDIF} {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenEditorFile START');{$ENDIF} Result:=mrCancel; CanAbort:=[ofProjectLoading,ofMultiOpen]*Flags<>[]; // replace macros if ofConvertMacros in Flags then begin if not GlobalMacroList.SubstituteStr(AFilename) then exit; AFilename:=ExpandFileNameUTF8(AFilename); end; // revert: use source editor filename if (ofRevert in Flags) and (PageIndex>=0) then AFilename := SourceEditorManager.SourceEditorsByPage[WindowIndex, PageIndex].FileName; if (ofRevert in Flags) then begin UnitIndex:=Project1.IndexOfFilename(AFilename); if (UnitIndex > 0) then begin NewUnitInfo:=Project1.Units[UnitIndex]; if (uifInternalFile in NewUnitInfo.Flags) then begin if (NewUnitInfo.OpenEditorInfoCount > 0) then begin NewEditorInfo := NewUnitInfo.OpenEditorInfo[0]; if MacroListViewer.MacroByFullName(AFileName) <> nil then NewUnitInfo.Source.Source := MacroListViewer.MacroByFullName(AFileName).GetAsSource; Result:=OpenFileInSourceEditor(NewEditorInfo, NewEditorInfo.PageIndex, NewEditorInfo.WindowIndex, Flags); exit; end; // unknown internal file exit(mrOk); end; end; end; if (ofInternalFile in Flags) then begin if (copy(AFileName, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive) then begin FilenameNoPath := AFileName; UnitIndex:=Project1.IndexOfFilename(AFilename); if (UnitIndex < 0) then begin NewBuf := CodeToolBoss.SourceCache.CreateFile(AFileName); if MacroListViewer.MacroByFullName(AFileName) <> nil then NewBuf.Source := MacroListViewer.MacroByFullName(AFileName).GetAsSource; NewUnitInfo:=TUnitInfo.Create(NewBuf); NewUnitInfo.DefaultSyntaxHighlighter := lshFreePascal; Project1.AddFile(NewUnitInfo,false); end else begin NewUnitInfo:=Project1.Units[UnitIndex]; end; NewUnitInfo.Flags := NewUnitInfo.Flags + [uifInternalFile]; if NewUnitInfo.OpenEditorInfoCount > 0 then begin NewEditorInfo := NewUnitInfo.OpenEditorInfo[0]; SourceEditorManager.ActiveSourceWindowIndex := NewEditorInfo.WindowIndex; SourceEditorManager.ActiveSourceWindow.PageIndex:= NewEditorInfo.PageIndex; end else begin NewEditorInfo := NewUnitInfo.GetClosedOrNewEditorInfo; Result:=OpenFileInSourceEditor(NewEditorInfo, PageIndex, WindowIndex, Flags); end; Result:=mrOK; exit; end; // unknown internal file => ignore exit(mrOK); end; // normalize filename AFilename:=TrimFilename(AFilename); DiskFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(AFilename); if DiskFilename<>AFilename then begin // the case is different DebugLn(['TLazSourceFileManager.OpenEditorFile Fixing file name: ',AFilename,' -> ',DiskFilename]); AFilename:=DiskFilename; end; // check if symlink and ask user open the real file instead ChooseSymlink(AFilename); FilenameNoPath:=ExtractFilename(AFilename); // check to not open directories if ((FilenameNoPath='') or (FilenameNoPath='.') or (FilenameNoPath='..')) then begin DebugLn(['TLazSourceFileManager.OpenEditorFile ignoring special file: ',AFilename]); exit; end; if DirectoryExistsUTF8(AFileName) then begin debugln(['TLazSourceFileManager.OpenEditorFile skipping directory ',AFileName]); exit(mrCancel); end; if ([ofAddToRecent,ofRevert,ofVirtualFile]*Flags=[ofAddToRecent]) and (AFilename<>'') and FilenameIsAbsolute(AFilename) then EnvironmentOptions.AddToRecentOpenFiles(AFilename); // check if this is a hidden unit: // if this is the main unit, it is already // loaded and needs only to be shown in the sourceeditor/formeditor if (not (ofRevert in Flags)) and (CompareFilenames(Project1.MainFilename,AFilename, not (ofVirtualFile in Flags))=0) then begin Result:=OpenMainUnit(PageIndex,WindowIndex,Flags); exit; end; // check for special files if ([ofRegularFile,ofRevert,ofProjectLoading]*Flags=[]) and FilenameIsAbsolute(AFilename) and FileExistsUTF8(AFilename) then begin // check if file is a lazarus project (.lpi) if (CompareFileExt(AFilename,'.lpi',false)=0) then begin case IDEQuestionDialog( lisOpenProject, Format(lisOpenTheProject, [AFilename]), mtConfirmation, [mrYes, lisOpenProject2, mrNoToAll, lisOpenAsXmlFile, mrCancel]) of mrYes: begin Result:=MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent]); exit; end; mrNoToAll: include(Flags, ofRegularFile); mrCancel: exit(mrCancel); end; end; // check if file is a lazarus package (.lpk) if (CompareFileExt(AFilename,'.lpk',false)=0) then begin case IDEQuestionDialog( lisOpenPackage, Format(lisOpenThePackage, [AFilename]), mtConfirmation, [mrYes, lisCompPalOpenPackage, mrNoToAll, lisOpenAsXmlFile, mrCancel]) of mrYes: begin Result:=PkgBoss.DoOpenPackageFile(AFilename,[pofAddToRecent],CanAbort); exit; end; mrCancel: exit(mrCancel); end; end; end; // check if the project knows this file if (ofRevert in Flags) then begin // revert UnknownFile := False; NewEditorInfo := Project1.EditorInfoWithEditorComponent( SourceEditorManager.SourceEditorsByPage[WindowIndex, PageIndex]); NewUnitInfo := NewEditorInfo.UnitInfo; UnitIndex:=Project1.IndexOf(NewUnitInfo); AFilename:=NewUnitInfo.Filename; if CompareFilenames(AFileName,DiskFilename)=0 then AFileName:=DiskFilename; if NewUnitInfo.IsVirtual then begin if (not (ofQuiet in Flags)) then begin IDEMessageDialog(lisRevertFailed, Format(lisFileIsVirtual, ['"', AFilename, '"']), mtInformation,[mbCancel]); end; Result:=mrCancel; exit; end; end else begin UnitIndex:=Project1.IndexOfFilename(AFilename); UnknownFile := (UnitIndex < 0); NewEditorInfo := nil; if not UnknownFile then begin NewUnitInfo:=Project1.Units[UnitIndex]; if AEditorInfo <> nil then NewEditorInfo := AEditorInfo else if (ofProjectLoading in Flags) then NewEditorInfo := NewUnitInfo.GetClosedOrNewEditorInfo else NewEditorInfo := NewUnitInfo.EditorInfo[0]; end; end; if (NewEditorInfo <> nil) and (ofAddToProject in Flags) and (not NewUnitInfo.IsPartOfProject) then begin NewUnitInfo.IsPartOfProject:=true; Project1.Modified:=true; end; if (NewEditorInfo <> nil) and (Flags * [ofProjectLoading, ofRevert] = []) and (NewEditorInfo.EditorComponent <> nil) then begin //DebugLn(['TLazSourceFileManager.OpenEditorFile file already open ',NewUnitInfo.Filename,' WindowIndex=',NewEditorInfo.WindowIndex,' PageIndex=',NewEditorInfo.PageIndex]); // file already open -> change source notebook page SourceEditorManager.ActiveSourceWindowIndex := NewEditorInfo.WindowIndex; SourceEditorManager.ActiveSourceWindow.PageIndex:= NewEditorInfo.PageIndex; if ofDoLoadResource in Flags then Result:=OpenResource else Result:=mrOk; exit; end; Reverting:=false; if ofRevert in Flags then begin Reverting:=true; Project1.BeginRevertUnit(NewUnitInfo); end; try // check if file exists if FilenameIsAbsolute(AFilename) and (not FileExistsUTF8(AFilename)) then begin // file does not exist if (ofRevert in Flags) then begin // revert failed, due to missing file if not (ofQuiet in Flags) then begin IDEMessageDialog(lisRevertFailed, Format(lisPkgMangFileNotFound, ['"', AFilename, '"']), mtError,[mbCancel]); end; Result:=mrCancel; exit; end else begin Result:=OpenNotExistingFile(AFilename,Flags); exit; end; end; // load the source if UnknownFile then begin // open unknown file // Never happens if ofRevert Handled:=false; Result:=OpenUnknownFile(AFilename,Flags,NewUnitInfo,Handled); if (Result<>mrOk) or Handled then exit; // the file was previously unknown, use the default EditorInfo if AEditorInfo <> nil then NewEditorInfo := AEditorInfo else if NewUnitInfo <> nil then NewEditorInfo := NewUnitInfo.GetClosedOrNewEditorInfo else NewEditorInfo := nil; end else begin // project knows this file => all the meta data is known // -> just load the source NewUnitInfo:=Project1.Units[UnitIndex]; LoadBufferFlags:=[lbfCheckIfText]; if FilenameIsAbsolute(AFilename) then begin if (not (ofUseCache in Flags)) then Include(LoadBufferFlags,lbfUpdateFromDisk); if ofRevert in Flags then Include(LoadBufferFlags,lbfRevert); end; Result:=LoadCodeBuffer(NewBuf,AFileName,LoadBufferFlags,CanAbort); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.OpenEditorFile failed LoadCodeBuffer: ',AFilename]); exit; end; NewUnitInfo.Source:=NewBuf; if FilenameIsPascalUnit(NewUnitInfo.Filename) then NewUnitInfo.ReadUnitNameFromSource(false); NewUnitInfo.Modified:=NewUnitInfo.Source.FileOnDiskNeedsUpdate; end; // check readonly NewUnitInfo.FileReadOnly:=FileExistsUTF8(NewUnitInfo.Filename) and (not FileIsWritable(NewUnitInfo.Filename)); //writeln('[TLazSourceFileManager.OpenEditorFile] B'); // open file in source notebook Result:=OpenFileInSourceEditor(NewEditorInfo, PageIndex, WindowIndex, Flags); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.OpenEditorFile failed DoOpenFileInSourceEditor: ',AFilename]); exit; end; //writeln('[TLazSourceFileManager.OpenEditorFile] C'); // open resource component (designer, form, datamodule, ...) if NewUnitInfo.OpenEditorInfoCount = 1 then Result:=OpenResource; if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.OpenEditorFile failed OpenResource: ',AFilename]); exit; end; finally if Reverting then Project1.EndRevertUnit(NewUnitInfo); end; Result:=mrOk; //writeln('TLazSourceFileManager.OpenEditorFile END "',AFilename,'"'); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenEditorFile END');{$ENDIF} end; function TLazSourceFileManager.OpenFileAtCursor(ActiveSrcEdit: TSourceEditor; ActiveUnitInfo: TUnitInfo): TModalResult; var FName,SPath: String; function FindFile(var FName: String; SPath: String): Boolean; // Searches for FName in SPath // If FName is not found, we'll check extensions pp and pas too // Returns true if found. FName contains the full file+path in that case var TempFile,TempPath,CurPath,FinalFile, Ext: String; p,c: Integer; PasExt: TPascalExtType; begin if SPath='' then SPath:='.'; Result:=true; TempPath:=SPath; while TempPath<>'' do begin p:=pos(';',TempPath); if p=0 then p:=length(TempPath)+1; CurPath:=copy(TempPath,1,p-1); Delete(TempPath,1,p); if CurPath='' then continue; CurPath:=AppendPathDelim(CurPath); if not FilenameIsAbsolute(CurPath) then begin if ActiveUnitInfo.IsVirtual then CurPath:=AppendPathDelim(Project1.ProjectDirectory)+CurPath else CurPath:=AppendPathDelim(ExtractFilePath(ActiveUnitInfo.Filename)) +CurPath; end; for c:=0 to 2 do begin // FPC searches first lowercase, then keeping case, then uppercase case c of 0: TempFile:=LowerCase(FName); 1: TempFile:=FName; 2: TempFile:=UpperCase(FName); end; if ExtractFileExt(TempFile)='' then begin for PasExt:=Low(TPascalExtType) to High(TPascalExtType) do begin Ext:=PascalExtension[PasExt]; FinalFile:=ExpandFileNameUTF8(CurPath+TempFile+Ext); if FileExistsUTF8(FinalFile) then begin FName:=FinalFile; exit; end; end; end else begin FinalFile:=ExpandFileNameUTF8(CurPath+TempFile); if FileExistsUTF8(FinalFile) then begin FName:=FinalFile; exit; end; end; end; end; Result:=false; end; function CheckIfIncludeDirectiveInFront(const Line: string; X: integer): boolean; var DirectiveEnd, DirectiveStart: integer; Directive: string; begin Result:=false; DirectiveEnd:=X; while (DirectiveEnd>1) and (Line[DirectiveEnd-1] in [' ',#9]) do dec(DirectiveEnd); DirectiveStart:=DirectiveEnd-1; while (DirectiveStart>0) and (Line[DirectiveStart]<>'$') do dec(DirectiveStart); Directive:=uppercase(copy(Line,DirectiveStart,DirectiveEnd-DirectiveStart)); if (Directive='$INCLUDE') or (Directive='$I') then begin if ((DirectiveStart>1) and (Line[DirectiveStart-1]='{')) or ((DirectiveStart>2) and (Line[DirectiveStart-2]='(') and (Line[DirectiveStart-1]='*')) then begin Result:=true; end; end; end; function GetFilenameAtRowCol(XY: TPoint; var IsIncludeDirective: boolean): string; var Line: string; Len, Stop: integer; StopChars: set of char; begin Result := ''; IsIncludeDirective:=false; if (XY.Y >= 1) and (XY.Y <= ActiveSrcEdit.EditorComponent.Lines.Count) then begin Line := ActiveSrcEdit.EditorComponent.Lines.Strings[XY.Y - 1]; Len := Length(Line); if (XY.X >= 1) and (XY.X <= Len + 1) then begin StopChars := [',',';',':','[',']','{','}','(',')',' ','''','"','`' ,'#','%','=','>']; Stop := XY.X; while (Stop <= Len) and (not (Line[Stop] in StopChars)) do Inc(Stop); while (XY.X > 1) and (not (Line[XY.X - 1] in StopChars)) do Dec(XY.X); if Stop > XY.X then begin Result := Copy(Line, XY.X, Stop - XY.X); IsIncludeDirective:=CheckIfIncludeDirectiveInFront(Line,XY.X); end; end; end; end; var IsIncludeDirective: boolean; BaseDir: String; NewFilename: string; Found: Boolean; AUnitName: String; InFilename: String; begin Result:=mrCancel; if (ActiveSrcEdit=nil) or (ActiveUnitInfo=nil) then exit; BaseDir:=ExtractFilePath(ActiveUnitInfo.Filename); // parse filename at cursor IsIncludeDirective:=false; Found:=false; FName:=GetFilenameAtRowCol(ActiveSrcEdit.EditorComponent.LogicalCaretXY, IsIncludeDirective); if FName='' then exit; // check if absolute filename if FilenameIsAbsolute(FName) then begin if FileExistsUTF8(FName) then Found:=true else exit; end; if (not Found) then begin if IsIncludeDirective then begin // search include file SPath:='.;'+CodeToolBoss.DefineTree.GetIncludePathForDirectory(BaseDir); if FindFile(FName,SPath) then Found:=true; end else if FilenameIsPascalSource(FName) or (ExtractFileExt(FName)='') then begin // search pascal unit AUnitName:=ExtractFileNameOnly(FName); InFilename:=FName; if ExtractFileExt(FName)='' then InFilename:=''; NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath( BaseDir,AUnitName,InFilename,true); if NewFilename<>'' then begin Found:=true; FName:=NewFilename; end; end; end; if (not Found) then begin // simple search relative to current unit InFilename:=AppendPathDelim(BaseDir)+FName; if FileExistsCached(InFilename) then begin Found:=true; FName:=InFilename; end; end; if (not Found) and (System.Pos('.',FName)>0) and (not IsIncludeDirective) then begin // for example 'SysUtils.CompareText' FName:=ActiveSrcEdit.EditorComponent.GetWordAtRowCol( ActiveSrcEdit.EditorComponent.LogicalCaretXY); if (FName<>'') and IsValidIdent(FName) then begin // search pascal unit AUnitName:=FName; InFilename:=''; NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath( BaseDir,AUnitName,InFilename,true); if NewFilename<>'' then begin Found:=true; FName:=NewFilename; end; end; end; if Found then begin // open InputHistories.SetFileDialogSettingsInitialDir(ExtractFilePath(FName)); Result:=OpenEditorFile(FName, -1, -1, nil, [ofAddToRecent]); end; end; function TLazSourceFileManager.InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult; var i:integer; HandlerResult: TModalResult; begin try Project1.BeginUpdate(true); try Project1.CompilerOptions.CompilerPath:='$(CompPath)'; if pfUseDefaultCompilerOptions in Project1.Flags then begin MainIDE.DoMergeDefaultProjectOptions(Project1); Project1.Flags:=Project1.Flags-[pfUseDefaultCompilerOptions]; end; Project1.AutoAddOutputDirToIncPath; MainIDE.UpdateCaption; if ProjInspector<>nil then ProjInspector.LazProject:=Project1; // add and load default required packages PkgBoss.OpenProjectDependencies(Project1,true); // rebuild codetools defines MainBuildBoss.SetBuildTargetProject1(false); // (i.e. remove old project specific things and create new) IncreaseCompilerParseStamp; Project1.DefineTemplates.AllChanged; Project1.DefineTemplates.Active:=true; DebugBoss.Reset; finally Project1.EndUpdate; end; Project1.BeginUpdate(true); try // create files if ProjectDesc.CreateStartFiles(Project1)<>mrOk then begin debugln('TLazSourceFileManager.NewProject ProjectDesc.CreateStartFiles failed'); end; if (Project1.MainUnitInfo<>nil) and ((Project1.FirstUnitWithEditorIndex=nil) or ([pfMainUnitHasCreateFormStatements,pfMainUnitHasTitleStatement]*Project1.Flags=[])) then begin // the project has not created any secondary files // or the project main source is not auto updated by the IDE OpenMainUnit(-1,-1,[]); end; // init resource files if not Project1.ProjResources.Regenerate(Project1.MainFilename, True, False,'') then DebugLn('TLazSourceFileManager.NewProject Project1.Resources.Regenerate failed'); finally Project1.EndUpdate; end; Result:=mrOk; finally // set all modified to false Project1.UpdateAllVisibleUnits; for i:=0 to Project1.UnitCount-1 do Project1.Units[i].ClearModifieds; Project1.Modified:=false; // call handlers HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1); if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then HandlerResult:=mrCancel; if (Result=mrOk) then Result:=HandlerResult; end; end; function TLazSourceFileManager.InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult; var EditorInfoIndex, i: Integer; NewBuf: TCodeBuffer; LastDesigner: TDesigner; AnUnitInfo: TUnitInfo; HandlerResult: TModalResult; AnEditorInfo: TUnitEditorInfo; begin EditorInfoIndex := 0; SourceEditorManager.IncUpdateLock; try Project1.BeginUpdate(true); try if ProjInspector<>nil then ProjInspector.LazProject:=Project1; // read project info file {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenProjectFile B3');{$ENDIF} Project1.ReadProject(AFilename); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenProjectFile B4');{$ENDIF} Result:=CompleteLoadingProjectInfo; finally Project1.EndUpdate; end; {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenProjectFile B5');{$ENDIF} if Result<>mrOk then exit; if Project1.MainUnitID>=0 then begin // read MainUnit Source Result:=LoadCodeBuffer(NewBuf,Project1.MainFilename, [lbfUpdateFromDisk,lbfRevert],false);// do not check if source is text case Result of mrOk: Project1.MainUnitInfo.Source:=NewBuf; mrIgnore: Project1.MainUnitInfo.Source:=CodeToolBoss.CreateFile(Project1.MainFilename); else exit(mrCancel); end; end; //debugln('TLazSourceFileManager.OpenProjectFile C'); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenProjectFile C');{$ENDIF} IncreaseCompilerParseStamp; // restore files while EditorInfoIndex < Project1.AllEditorsInfoCount do begin // TProject.ReadProject sorts alle UnitEditorInfos AnEditorInfo := Project1.AllEditorsInfo[EditorInfoIndex]; AnUnitInfo := AnEditorInfo.UnitInfo; if (not AnUnitInfo.Loaded) or (AnEditorInfo.PageIndex < 0) then begin inc(EditorInfoIndex); Continue; end; // reopen file if (not AnUnitInfo.IsPartOfProject) and (not FileExistsCached(AnUnitInfo.Filename)) then begin // this file does not exist, but is not important => silently ignore end else begin // reopen file // This will adjust Page/WindowIndex if they are not continious Result:=OpenEditorFile(AnUnitInfo.Filename, -1, AnEditorInfo.WindowIndex, AnEditorInfo, [ofProjectLoading,ofMultiOpen,ofOnlyIfExists]); if Result=mrAbort then exit; end; if not ((AnUnitInfo.Filename<>'') and (AnEditorInfo.EditorComponent <> nil)) then begin // failed to open AnEditorInfo.PageIndex := -1; // if failed entirely -> mark as unloaded, so that next time it will not be tried again if AnUnitInfo.OpenEditorInfoCount = 0 then AnUnitInfo.Loaded := False; end; inc(EditorInfoIndex); end; // while EditorInfoIndex < Project1.AllEditorsInfoCount Result:=mrCancel; //debugln('TLazSourceFileManager.OpenProjectFile D'); // set active editor source editor for i := 0 to Project1.AllEditorsInfoCount - 1 do begin AnEditorInfo := Project1.AllEditorsInfo[i]; if AnEditorInfo.IsVisibleTab then begin if AnEditorInfo.WindowIndex >= SourceEditorManager.SourceWindowCount then begin // session info is invalid (buggy lps file?) => auto fix AnEditorInfo.IsVisibleTab:=false; AnEditorInfo.WindowIndex:=-1; end; if (AnEditorInfo.WindowIndex < 0) then continue; if (SourceEditorManager.SourceWindows[AnEditorInfo.WindowIndex] <> nil) then SourceEditorManager.SourceWindows[AnEditorInfo.WindowIndex].PageIndex := AnEditorInfo.PageIndex; end; end; if (Project1.ActiveWindowIndexAtStart<0) or (Project1.ActiveWindowIndexAtStart >= SourceEditorManager.SourceWindowCount) then begin // session info is invalid (buggy lps file?) => auto fix Project1.ActiveWindowIndexAtStart := 0; end; if (Project1.ActiveWindowIndexAtStart >= 0) and (Project1.ActiveWindowIndexAtStart < SourceEditorManager.SourceWindowCount) then begin SourceEditorManager.ActiveSourceWindow := SourceEditorManager.SourceWindows[Project1.ActiveWindowIndexAtStart]; SourceEditorManager.ShowActiveWindowOnTop(True); end; if ([ofDoNotLoadResource]*Flags=[]) and ( (not Project1.AutoOpenDesignerFormsDisabled) and EnvironmentOptions.AutoCreateFormsOnOpen and (SourceEditorManager.ActiveEditor<>nil) ) then begin // auto open form of active unit AnUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor); if AnUnitInfo<>nil then Result:=LoadLFM(AnUnitInfo,[ofProjectLoading,ofMultiOpen,ofOnlyIfExists], [cfSaveDependencies]); end; // select a form (object inspector, formeditor, control selection) if MainIDE.LastFormActivated<>nil then begin LastDesigner:=TDesigner(MainIDE.LastFormActivated.Designer); debugln(['TLazSourceFileManager.OpenProjectFile select form in designer: ', DbgSName(MainIDE.LastFormActivated),' ',DbgSName(MainIDE.LastFormActivated.Designer)]); LastDesigner.SelectOnlyThisComponent(LastDesigner.LookupRoot); end; // set all modified to false Project1.UpdateAllVisibleUnits; Project1.ClearModifieds(true); IncreaseCompilerParseStamp; IDEProtocolOpts.LastProjectLoadingCrashed := False; Result:=mrOk; finally SourceEditorManager.DecUpdateLock; if (Result<>mrOk) and (Project1<>nil) then begin // mark all files, that are left to open as unloaded: for i := EditorInfoIndex to Project1.AllEditorsInfoCount - 1 do begin AnEditorInfo := Project1.AllEditorsInfo[i]; AnEditorInfo.PageIndex := -1; AnUnitInfo := AnEditorInfo.UnitInfo; if AnUnitInfo.Loaded and (AnUnitInfo.OpenEditorInfoCount = 0) then AnUnitInfo.Loaded := false; end; end; // call handlers HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1); if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then HandlerResult:=mrCancel; if (Result=mrOk) then Result:=HandlerResult; end; if Result=mrAbort then exit; //debugln('TLazSourceFileManager.OpenProjectFile end CodeToolBoss.ConsistencyCheck=',IntToStr(CodeToolBoss.ConsistencyCheck)); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.OpenProjectFile end');{$ENDIF} end; procedure TLazSourceFileManager.NewProjectFromFile; var OpenDialog:TOpenDialog; AFilename: string; PreReadBuf: TCodeBuffer; Filter: String; Begin OpenDialog:=TOpenDialog.Create(nil); try InputHistories.ApplyFileDialogSettings(OpenDialog); OpenDialog.Title:=lisChooseProgramSourcePpPasLpr; OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist,ofFileMustExist]; Filter := lisLazarusUnit + ' (*.pas;*.pp;*.p)|*.pas;*.pp;*.p' + '|' + lisLazarusProjectSource + ' (*.lpr)|*.lpr'; Filter:=Filter+ '|' + dlgAllFiles + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask; OpenDialog.Filter := Filter; if OpenDialog.Execute then begin AFilename:=ExpandFileNameUTF8(OpenDialog.Filename); if not FilenameIsPascalSource(AFilename) then begin IDEMessageDialog(lisPkgMangInvalidFileExtension, lisProgramSourceMustHaveAPascalExtensionLikePasPpOrLp, mtError,[mbOk],''); exit; end; if mrOk<>LoadCodeBuffer(PreReadBuf,AFileName, [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert],false) then exit; if CreateProjectForProgram(PreReadBuf)=mrOk then begin exit; end; end; finally InputHistories.StoreFileDialogSettings(OpenDialog); OpenDialog.Free; end; end; function TLazSourceFileManager.CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult; var NewProjectDesc: TProjectDescriptor; begin //writeln('[TLazSourceFileManager.DoCreateProjectForProgram] A ',ProgramBuf.Filename); if (Project1 <> nil) and (not MainIDE.DoResetToolStatus([rfInteractive, rfSuccessOnTrigger])) then exit; Result:=SaveProjectIfChanged; if Result=mrAbort then exit; // let user choose the program type NewProjectDesc:=nil; if ChooseNewProject(NewProjectDesc)<>mrOk then exit; // close old project If Project1<>nil then begin if CloseProject=mrAbort then begin Result:=mrAbort; exit; end; end; // reload file (if the file was open in the IDE, closeproject unloaded it) ProgramBuf.Reload; // switch codetools to new project directory CodeToolBoss.GlobalValues.Variables[ExternalMacroStart+'ProjPath']:= ExpandFileNameUTF8(ExtractFilePath(ProgramBuf.Filename)); // create a new project Project1:=MainIDE.CreateProjectObject(NewProjectDesc,ProjectDescriptorProgram); Result:=InitProjectForProgram(ProgramBuf); //writeln('[TLazSourceFileManager.DoCreateProjectForProgram] END'); end; function TLazSourceFileManager.InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult; var MainUnitInfo: TUnitInfo; begin Project1.BeginUpdate(true); try if ProjInspector<>nil then ProjInspector.LazProject:=Project1; MainUnitInfo:=Project1.MainUnitInfo; MainUnitInfo.Source:=ProgramBuf; Project1.ProjectInfoFile:=ChangeFileExt(ProgramBuf.Filename,'.lpi'); MainIDE.DoMergeDefaultProjectOptions(Project1); MainIDE.UpdateCaption; IncreaseCompilerParseStamp; // add and load default required packages PkgBoss.OpenProjectDependencies(Project1,true); Result:=CompleteLoadingProjectInfo; if Result<>mrOk then exit; finally Project1.EndUpdate; end; // show program unit Result:=OpenEditorFile(ProgramBuf.Filename,-1,-1, nil, [ofAddToRecent,ofRegularFile]); if Result=mrAbort then exit; Result:=mrOk; end; function TLazSourceFileManager.SaveProject(Flags: TSaveFlags):TModalResult; var i: integer; AnUnitInfo: TUnitInfo; SaveFileFlags: TSaveFlags; SrcEdit: TSourceEditor; begin Result:=mrCancel; if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin Result:=mrAbort; exit; end; SaveSourceEditorChangesToCodeCache(nil); //DebugLn('TLazSourceFileManager.SaveProject A SaveAs=',dbgs(sfSaveAs in Flags),' SaveToTestDir=',dbgs(sfSaveToTestDir in Flags),' ProjectInfoFile=',Project1.ProjectInfoFile); Result:=MainIDE.DoCheckFilesOnDisk(true); if Result in [mrCancel,mrAbort] then exit; if CheckMainSrcLCLInterfaces(sfQuietUnitCheck in Flags)<>mrOk then exit(mrCancel); // if this is a virtual project then save first the project info file // to get a project directory if Project1.IsVirtual and ([sfSaveToTestDir,sfDoNotSaveVirtualFiles]*Flags=[]) then begin Result:=SaveProjectInfo(Flags); if Result in [mrCancel,mrAbort] then exit; end; if (not (sfDoNotSaveVirtualFiles in Flags)) then begin // check that all new units are saved first to get valid filenames // (this can alter the mainunit: e.g. used unit names) for i:=0 to Project1.UnitCount-1 do begin AnUnitInfo:=Project1.Units[i]; if (AnUnitInfo.Loaded) and AnUnitInfo.IsVirtual and AnUnitInfo.IsPartOfProject and (Project1.MainUnitID<>i) and (AnUnitInfo.OpenEditorInfoCount > 0) then begin SaveFileFlags:=[sfSaveAs,sfProjectSaving] +[sfCheckAmbiguousFiles]*Flags; if sfSaveToTestDir in Flags then begin if AnUnitInfo.IsPartOfProject or AnUnitInfo.IsVirtual then Include(SaveFileFlags,sfSaveToTestDir); end; Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, SaveFileFlags); if Result in [mrCancel,mrAbort] then exit; end; end; end; Result:=SaveProjectInfo(Flags); if Result in [mrCancel,mrAbort] then exit; // save all editor files for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin SrcEdit:=SourceEditorManager.SourceEditors[i]; AnUnitInfo:=Project1.UnitWithEditorComponent(SrcEdit); if (Project1.MainUnitID>=0) and (Project1.MainUnitInfo = AnUnitInfo) then continue; SaveFileFlags:=[sfProjectSaving] +Flags*[sfCheckAmbiguousFiles]; if AnUnitInfo = nil then begin // consistency check DebugLn(['TLazSourceFileManager.SaveProject - unit not found for page ',i,' File="',SrcEdit.FileName,'"']); DumpStack; end else begin if AnUnitInfo.IsVirtual then begin if (sfSaveToTestDir in Flags) then Include(SaveFileFlags,sfSaveToTestDir) else continue; end; end; Result:=SaveEditorFile(SourceEditorManager.SourceEditors[i], SaveFileFlags); if Result=mrAbort then exit; // mrCancel: continue saving other files end; // update all lrs files if sfSaveToTestDir in Flags then MainBuildBoss.UpdateProjectAutomaticFiles(EnvironmentOptions.GetParsedTestBuildDirectory) else MainBuildBoss.UpdateProjectAutomaticFiles(''); // everything went well => clear all modified flags Project1.ClearModifieds(true); // update menu and buttons state MainIDE.UpdateSaveMenuItemsAndButtons(true); //DebugLn('TLazSourceFileManager.SaveProject End'); Result:=mrOk; end; function TLazSourceFileManager.SaveProjectIfChanged: TModalResult; begin if SomethingOfProjectIsModified then begin if IDEMessageDialog(lisProjectChanged, Format(lisSaveChangesToProject, [Project1.GetTitleOrName]), mtconfirmation, [mbYes, mbNo, mbCancel])=mrYes then begin if SaveProject([])=mrAbort then begin Result:=mrAbort; exit; end; end; end; Result:=mrOk; end; function TLazSourceFileManager.CloseProject: TModalResult; begin //writeln('TLazSourceFileManager.CloseProject A'); {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.CloseProject A');{$ENDIF} Result:=DebugBoss.DoStopProject; if Result<>mrOk then begin debugln('TLazSourceFileManager.CloseProject DebugBoss.DoStopProject failed'); exit; end; // call handlers Result:=MainIDE.DoCallProjectChangedHandler(lihtProjectClose, Project1); if Result=mrAbort then exit; // close all loaded files SourceEditorManager.IncUpdateLock; try while SourceEditorManager.SourceEditorCount > 0 do begin Result:=CloseEditorFile(SourceEditorManager.SourceEditors[0],[cfProjectClosing]); if Result=mrAbort then exit; end; finally SourceEditorManager.DecUpdateLock; end; {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.CloseProject B');{$ENDIF} IncreaseCompilerParseStamp; // close Project if ProjInspector<>nil then ProjInspector.LazProject:=nil; FreeThenNil(Project1); if IDEMessagesWindow<>nil then IDEMessagesWindow.Clear; {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TLazSourceFileManager.CloseProject C');{$ENDIF} Result:=mrOk; //writeln('TLazSourceFileManager.CloseProject end ',CodeToolBoss.ConsistencyCheck); end; procedure TLazSourceFileManager.OpenProject(aMenuItem: TIDEMenuItem); var OpenDialog:TOpenDialog; AFileName: string; LoadFlags: TLoadBufferFlags; PreReadBuf: TCodeBuffer; SourceType: String; LPIFilename: String; begin if Assigned(aMenuItem) and (aMenuItem.Section=itmProjectRecentOpen) then begin AFileName:=ExpandFileNameUTF8(aMenuItem.Caption); if MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent])=mrOk then begin AddRecentProjectFileToEnvironment(AFilename); end else begin // open failed if not FileExistsUTF8(AFilename) then begin EnvironmentOptions.RemoveFromRecentProjectFiles(AFilename); end else AddRecentProjectFileToEnvironment(AFilename); end; end else begin OpenDialog:=TOpenDialog.Create(nil); try InputHistories.ApplyFileDialogSettings(OpenDialog); OpenDialog.Title:=lisOpenProjectFile+' (*.lpi)'; OpenDialog.Filter := lisLazarusProjectInfoFile+' (*.lpi)|*.lpi|' +lisAllFiles+'|'+GetAllFilesMask; if OpenDialog.Execute then begin AFilename:=ExpandFileNameUTF8(OpenDialog.Filename); if FileUtil.CompareFileExt(AFilename,'.lpi')<>0 then begin // not a lpi file // check if it is a program source // load the source LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert]; if LoadCodeBuffer(PreReadBuf,AFileName,LoadFlags,true)<>mrOk then exit; // check if unit is a program SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false); if (SysUtils.CompareText(SourceType,'PROGRAM')=0) or (SysUtils.CompareText(SourceType,'LIBRARY')=0) then begin // source is a program // either this is a lazarus project // or it is not yet a lazarus project ;) LPIFilename:=ChangeFileExt(AFilename,'.lpi'); if FileExistsUTF8(LPIFilename) then begin if IDEQuestionDialog(lisProjectInfoFileDetected, Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP, [ AFilename]), mtConfirmation, [mrOk, lisOpenProject2, mrCancel]) <>mrOk then exit; AFilename:=LPIFilename; end else begin if IDEQuestionDialog(lisFileHasNoProject, Format(lisTheFileIsNotALazarusProjectCreateANewProjectForThi, [ '"', AFilename, '"', LineEnding, '"'+lowercase(SourceType)+'"']), mtConfirmation, [mrYes, lisCreateProject, mrCancel])<>mrYes then exit; CreateProjectForProgram(PreReadBuf); exit; end; end; end; MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent]); end; InputHistories.StoreFileDialogSettings(OpenDialog); finally OpenDialog.Free; end; end; end; procedure TLazSourceFileManager.CloseAll; var i, NeedSave, Idx: Integer; r: TModalResult; Ed: TSourceEditor; begin NeedSave := 0; for i := 0 to SourceEditorManager.UniqueSourceEditorCount - 1 do begin if CheckEditorNeedsSave(SourceEditorManager.UniqueSourceEditors[i], False) then begin inc(NeedSave); if NeedSave = 1 then Idx := i; end; end; if NeedSave = 1 then begin Ed := TSourceEditor(SourceEditorManager.UniqueSourceEditors[Idx]); r := IDEQuestionDialog(lisSourceModified, Format(lisSourceOfPageHasChangedSave, ['"', Ed.PageName, '"']), mtConfirmation, [mrYes, lisMenuSave, mrNo, lisDiscardChanges, mrAbort]); case r of mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]); mrNo: ; // don't save mrAbort: exit; end; end else if NeedSave > 1 then begin for i := 0 to SourceEditorManager.UniqueSourceEditorCount - 1 do begin if CheckEditorNeedsSave(SourceEditorManager.UniqueSourceEditors[i], False) then begin dec(NeedSave); Ed := TSourceEditor(SourceEditorManager.UniqueSourceEditors[i]); r := IDEQuestionDialog(lisSourceModified, Format(lisSourceOfPageHasChangedSaveExtended, ['"', Ed.PageName, '"', NeedSave]), mtConfirmation, [mrYes, lisMenuSave, mrAll, lisSaveAll, mrNo, lisDiscardChanges, mrIgnore, lisDiscardChangesAll, mrAbort]); case r of mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]); mrNo: ; // don't save mrAll: begin MainIDE.DoSaveAll([]); break end; mrIgnore: break; // don't save anymore mrAbort: exit; end; end; end; end; SourceEditorManager.IncUpdateLock; try while (SourceEditorManager.SourceEditorCount > 0) and (CloseEditorFile(SourceEditorManager.SourceEditors[0], []) = mrOk) do ; finally SourceEditorManager.DecUpdateLock; end; end; function TLazSourceFileManager.CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor; NewOwner: TObject; NewFilename: string; var NewCodeBuffer: TCodeBuffer; var NewUnitName: string): TModalResult; var NewShortFilename: String; NewFileExt: String; SearchFlags: TSearchIDEFileFlags; begin //debugln('TLazSourceFileManager.CreateNewCodeBuffer START NewFilename=',NewFilename,' ',Descriptor.DefaultFilename,' ',Descriptor.ClassName); NewUnitName:=''; NewCodeBuffer:=nil; if NewFilename='' then begin // create a new unique filename SearchFlags:=[siffCheckAllProjects]; if Descriptor.IsPascalUnit then begin if NewUnitName='' then NewUnitName:=Descriptor.DefaultSourceName; NewShortFilename:=lowercase(NewUnitName); NewFileExt:=Descriptor.DefaultFileExt; SearchFlags:=SearchFlags+[siffIgnoreExtension]; end else begin NewFilename:=ExtractFilename(Descriptor.DefaultFilename); NewShortFilename:=ExtractFilenameOnly(NewFilename); NewFileExt:=ExtractFileExt(NewFilename); SearchFlags:=[]; end; NewFilename:=MainIDE.CreateNewUniqueFilename(NewShortFilename, NewFileExt,NewOwner,SearchFlags,true); if NewFilename='' then RaiseException(''); NewShortFilename:=ExtractFilenameOnly(NewFilename); // use as unitname the NewShortFilename, but with the case of the // original unitname. e.g. 'unit12.pas' becomes 'Unit12.pas' if Descriptor.IsPascalUnit then begin NewUnitName:=ChompEndNumber(NewUnitName); NewUnitName:=NewUnitName+copy(NewShortFilename,length(NewUnitName)+1, length(NewShortFilename)); end; end; //debugln('TLazSourceFileManager.CreateNewCodeBuffer NewFilename=',NewFilename,' NewUnitName=',NewUnitName); if FilenameIsPascalUnit(NewFilename) then begin if NewUnitName='' then NewUnitName:=ExtractFileNameOnly(NewFilename); if EnvironmentOptions.CharcaseFileAction in [ccfaAsk, ccfaAutoRename] then NewFilename:=ExtractFilePath(NewFilename) +lowercase(ExtractFileName(NewFilename)); end; NewCodeBuffer:=CodeToolBoss.CreateFile(NewFilename); if NewCodeBuffer=nil then exit(mrCancel); Result:=mrOk; end; function TLazSourceFileManager.CreateNewForm(NewUnitInfo: TUnitInfo; AncestorType: TPersistentClass; ResourceCode: TCodeBuffer; UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult; var NewComponent: TComponent; new_x, new_y: integer; p: TPoint; r: TRect; begin if not AncestorType.InheritsFrom(TComponent) then RaiseException('TLazSourceFileManager.CreateNewForm invalid AncestorType'); //debugln('TLazSourceFileManager.CreateNewForm START ',NewUnitInfo.Filename,' ',AncestorType.ClassName,' ',dbgs(ResourceCode<>nil)); // create a buffer for the new resource file and for the LFM file if ResourceCode=nil then begin ResourceCode:= CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename, ResourceFileExt)); end; //debugln('TLazSourceFileManager.CreateNewForm B ',ResourceCode.Filename); ResourceCode.Source:='{ '+LRSComment+' }'; CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename,'.lfm')); // clear formeditor FormEditor1.ClearSelection; // Figure out where we want to put the new form // if there is more place left of the OI put it left, otherwise right p:=Point(0,0); if ObjectInspector1<>nil then begin p:=ObjectInspector1.ClientOrigin; new_x:=p.x; new_y:=p.Y+10; end else begin new_x:=200; new_y:=100; end; if new_x>Screen.Width div 2 then new_x:=new_x-500 else if ObjectInspector1<>nil then new_x:=new_x+ObjectInspector1.Width; r:=Screen.PrimaryMonitor.WorkareaRect; new_x:=Max(r.Left,Min(new_x,r.Right-400)); new_y:=Max(r.Top,Min(new_y,r.Bottom-400)); // create jit component NewComponent := FormEditor1.CreateComponent(nil,TComponentClass(AncestorType), NewUnitInfo.CreateUnitName, new_x, new_y, 0,0,DisableAutoSize); if NewComponent=nil then begin DebugLn(['TLazSourceFileManager.CreateNewForm FormEditor1.CreateComponent failed ',dbgsName(TComponentClass(AncestorType))]); exit(mrCancel); end; FormEditor1.SetComponentNameAndClass(NewComponent, NewUnitInfo.ComponentName,'T'+NewUnitInfo.ComponentName); if NewComponent is TCustomForm then TControl(NewComponent).Visible := False; if (NewComponent is TControl) and (csSetCaption in TControl(NewComponent).ControlStyle) then TControl(NewComponent).Caption:=NewComponent.Name; NewUnitInfo.Component := NewComponent; MainIDE.CreateDesignerForComponent(NewUnitInfo,NewComponent); NewUnitInfo.ComponentName:=NewComponent.Name; NewUnitInfo.ComponentResourceName:=NewUnitInfo.ComponentName; if UseCreateFormStatements and NewUnitInfo.IsPartOfProject and Project1.AutoCreateForms and (pfMainUnitHasCreateFormStatements in Project1.Flags) then begin Project1.AddCreateFormToProjectFile(NewComponent.ClassName, NewComponent.Name); end; Result:=mrOk; end; function TLazSourceFileManager.NewUniqueComponentName(Prefix: string): string; function SearchProject(AProject: TProject; const Identifier: string): boolean; var i: Integer; AnUnitInfo: TUnitInfo; begin if AProject=nil then exit(false); Result:=true; for i:=0 to AProject.UnitCount-1 do begin AnUnitInfo:=AProject.Units[i]; if (AnUnitInfo.Component<>nil) then begin if CompareText(AnUnitInfo.Component.Name,Identifier)=0 then exit; if CompareText(AnUnitInfo.Component.ClassName,Identifier)=0 then exit; end else if (AnUnitInfo.ComponentName<>'') and ((AnUnitInfo.IsPartOfProject) or AnUnitInfo.Loaded) then begin if SysUtils.CompareText(AnUnitInfo.Unit_Name,Identifier)=0 then exit; if SysUtils.CompareText(AnUnitInfo.ComponentName,Identifier)=0 then exit; end; end; Result:=false; end; function SearchPackage(APackage: TLazPackage; const Identifier: string): boolean; var i: Integer; PkgFile: TPkgFile; begin if APackage=nil then exit(false); Result:=true; if SysUtils.CompareText(APackage.Name,Identifier)=0 then exit; for i:=0 to APackage.FileCount-1 do begin PkgFile:=APackage.Files[i]; if SysUtils.CompareText(PkgFile.Unit_Name,Identifier)=0 then exit; end; Result:=false; end; function IdentifierExists(Identifier: string): boolean; var i: Integer; begin Result:=true; if GetClass(Identifier)<>nil then exit; if SearchProject(Project1,Identifier) then exit; for i:=0 to PackageGraph.Count-1 do if SearchPackage(PackageGraph[i],Identifier) then exit; Result:=false; end; function IdentifierIsOk(Identifier: string): boolean; begin Result:=false; if (Identifier='') or not IsValidIdent(Identifier) then exit; if AllKeyWords.DoIdentifier(PChar(Identifier)) then exit; if IdentifierExists(Identifier) then exit; if IdentifierExists('T'+Identifier) then exit; Result:=true; end; var i: Integer; begin if IdentifierIsOk(Prefix) then exit(Prefix); while (Prefix<>'') and (Prefix[length(Prefix)] in ['0'..'9']) do System.Delete(Prefix,length(Prefix),1); if (Prefix='') or (not IsValidIdent(Prefix)) then Prefix:='Resource'; i:=0; repeat inc(i); Result:=Prefix+IntToStr(i); until IdentifierIsOk(Result); end; function TLazSourceFileManager.ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult; var SaveDialog: TSaveDialog; SaveAsFilename, SaveAsFileExt, NewFilename, NewUnitName, NewFilePath, AlternativeUnitName: string; ACaption, AText: string; SrcEdit: TSourceEditor; FileWithoutPath: String; PkgDefaultDirectory: String; OldUnitName: String; IsPascal: Boolean; Filter: String; AllEditorExt: string; AllFilter: string; begin if (AnUnitInfo<>nil) and (AnUnitInfo.OpenEditorInfoCount>0) then SrcEdit := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent) else SrcEdit:=nil; //debugln('TLazSourceFileManager.ShowSaveFileAsDialog ',AnUnitInfo.Filename); // try to keep the old filename and extension SaveAsFileExt:=ExtractFileExt(AFileName); if (SaveAsFileExt='') and (SrcEdit<>nil) then begin if (SrcEdit.SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then SaveAsFileExt:=PascalExtension[EnvironmentOptions.PascalFileExtension] else SaveAsFileExt:=EditorOpts.HighlighterList.GetDefaultFilextension( SrcEdit.SyntaxHighlighterType); end; IsPascal:=FilenameIsPascalSource(AFilename); if IsPascal then begin if AnUnitInfo<>nil then OldUnitName:=AnUnitInfo.ParseUnitNameFromSource(false) else OldUnitName:=ExtractFileNameOnly(AFilename); end else OldUnitName:=''; //debugln('TLazSourceFileManager.ShowSaveFileAsDialog sourceunitname=',OldUnitName); SaveAsFilename:=OldUnitName; if SaveAsFilename='' then SaveAsFilename:=ExtractFileNameOnly(AFilename); if SaveAsFilename='' then SaveAsFilename:=lisnoname; //suggest lowercased name if user wants so if EnvironmentOptions.LowercaseDefaultFilename = true then SaveAsFilename:=LowerCase(SaveAsFilename); // let user choose a filename SaveDialog:=TSaveDialog.Create(nil); try InputHistories.ApplyFileDialogSettings(SaveDialog); SaveDialog.Title:=lisSaveSpace+SaveAsFilename+' (*'+SaveAsFileExt+')'; SaveDialog.FileName:=SaveAsFilename+SaveAsFileExt; Filter := lisLazarusUnit + ' (*.pas;*.pp)|*.pas;*.pp'; if (SaveAsFileExt='.lpi') then Filter:=Filter+ '|' + lisLazarusProject + ' (*.lpi)|*.lpi'; if (SaveAsFileExt='.lfm') or (SaveAsFileExt='.dfm') then Filter:=Filter+ '|' + lisLazarusForm + ' (*.lfm;*.dfm)|*.lfm;*.dfm'; if (SaveAsFileExt='.lpk') then Filter:=Filter+ '|' + lisLazarusPackage + ' (*.lpk)|*.lpk'; if (SaveAsFileExt='.lpr') then Filter:=Filter+ '|' + lisLazarusProjectSource + ' (*.lpr)|*.lpr'; // append a filter for all editor files CreateFileDialogFilterForSourceEditorFiles(Filter,AllEditorExt,AllFilter); if AllEditorExt<>'' then Filter:=Filter+ '|' + lisEditorFileTypes + ' (' + AllEditorExt + ')|' + AllEditorExt; // append an any file filter *.* Filter:=Filter+ '|' + dlgAllFiles + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask; // prepend an all filter Filter:= lisLazarusFile + ' ('+AllFilter+')|' + AllFilter + '|' + Filter; SaveDialog.Filter := Filter; // if this is a project file, start in project directory if (AnUnitInfo=nil) or (AnUnitInfo.IsPartOfProject and (not Project1.IsVirtual) and (not FileIsInPath(SaveDialog.InitialDir,Project1.ProjectDirectory))) then begin SaveDialog.InitialDir:=Project1.ProjectDirectory; end; // if this is a package file, then start in package directory PkgDefaultDirectory:= PkgBoss.GetDefaultSaveDirectoryForFile(AFilename); if (PkgDefaultDirectory<>'') and (not FileIsInPath(SaveDialog.InitialDir,PkgDefaultDirectory)) then SaveDialog.InitialDir:=PkgDefaultDirectory; // show save dialog if (not SaveDialog.Execute) or (ExtractFileName(SaveDialog.Filename)='') then begin // user cancels Result:=mrCancel; exit; end; NewFilename:=ExpandFileNameUTF8(SaveDialog.Filename); //debugln(['TLazSourceFileManager.ShowSaveFileAsDialog SaveDialog.Filename="',SaveDialog.Filename,'" NewFilename="',NewFilename,'"']); finally InputHistories.StoreFileDialogSettings(SaveDialog); SaveDialog.Free; end; // check file extension if ExtractFileExt(NewFilename)='' then begin NewFilename:=NewFilename+SaveAsFileExt; end; // check file path NewFilePath:=ExtractFilePath(NewFilename); if not DirPathExists(NewFilePath) then begin ACaption:=lisEnvOptDlgDirectoryNotFound; AText:=Format(lisTheDestinationDirectoryDoesNotExist, [LineEnding, '"', NewFilePath, '"']); Result:=IDEMessageDialogAb(ACaption, AText, mtConfirmation,[mbCancel],CanAbort); exit; end; // check unitname if FilenameIsPascalUnit(NewFilename) then begin NewUnitName:=ExtractFileNameOnly(NewFilename); // do not rename the unit if new filename differs from its name only in case if LowerCase(OldUnitName)=NewUnitName then NewUnitName:=OldUnitName; if NewUnitName='' then begin Result:=mrCancel; exit; end; if not IsValidUnitName(NewUnitName) then begin AlternativeUnitName:=NameToValidIdentifier(NewUnitName); Result:=IDEMessageDialogAb(lisInvalidPascalIdentifierCap, Format(lisInvalidPascalIdentifierText,[NewUnitName,AlternativeUnitName]), mtWarning,[mbIgnore,mbCancel],CanAbort); if Result in [mrCancel,mrAbort] then exit; NewUnitName:=AlternativeUnitName; end; if Project1.IndexOfUnitWithName(NewUnitName,true,AnUnitInfo)>=0 then begin Result:=IDEQuestionDialogAb(lisUnitNameAlreadyExistsCap, Format(lisTheUnitAlreadyExists, ['"', NewUnitName, '"']), mtConfirmation, [mrIgnore, lisForceRenaming, mrCancel, lisCancelRenaming, mrAbort, lisAbortAll], not CanAbort); if Result=mrIgnore then Result:=mrCancel else exit; end; end else begin NewUnitName:=''; end; // check filename if FilenameIsPascalUnit(NewFilename) then begin FileWithoutPath:=ExtractFileName(NewFilename); // check if file should be auto renamed if EnvironmentOptions.CharcaseFileAction = ccfaAsk then begin if lowercase(FileWithoutPath)<>FileWithoutPath then begin Result:=IDEQuestionDialogAb(lisRenameFile, Format(lisThisLooksLikeAPascalFileItIsRecommendedToUseLowerC, [LineEnding, LineEnding]), mtWarning, [mrYes, lisRenameToLowercase, mrNoToAll, lisKeepName, mrAbort, lisAbortAll], not CanAbort); if Result=mrYes then NewFileName:=ExtractFilePath(NewFilename)+lowercase(FileWithoutPath); Result:=mrOk; end; end else begin if EnvironmentOptions.CharcaseFileAction = ccfaAutoRename then NewFileName:=ExtractFilePath(NewFilename)+lowercase(FileWithoutPath); end; end; // check overwrite existing file if ((not FilenameIsAbsolute(AFilename)) or (CompareFilenames(NewFilename,AFilename)<>0)) and FileExistsUTF8(NewFilename) then begin ACaption:=lisOverwriteFile; AText:=Format(lisAFileAlreadyExistsReplaceIt, ['"', NewFilename, '"', LineEnding]); Result:=IDEQuestionDialogAb(ACaption, AText, mtConfirmation, [mrYes, lisOverwriteFileOnDisk, mrCancel, mrAbort, lisAbortAll], not CanAbort); if Result=mrCancel then exit; end; if AnUnitInfo<>nil then begin // rename unit Result:=RenameUnit(AnUnitInfo,NewFilename,NewUnitName,LFMCode,LRSCode); AFilename:=AnUnitInfo.Filename; if Result<>mrOk then exit; end else begin Result:=mrOk; AFilename:=NewFilename; end; end; type TLRTGrubber = class(TObject) private FGrubbed: TStrings; FWriter: TWriter; public constructor Create(TheWriter: TWriter); destructor Destroy; override; procedure Grub(Sender: TObject; const Instance: TPersistent; PropInfo: PPropInfo; var Content: string); property Grubbed: TStrings read FGrubbed; property Writer: TWriter read FWriter write FWriter; end; constructor TLRTGrubber.Create(TheWriter: TWriter); begin inherited Create; FGrubbed:=TStringList.Create; FWriter:=TheWriter; FWriter.OnWriteStringProperty:=@Grub; end; destructor TLRTGrubber.Destroy; begin FGrubbed.Free; inherited Destroy; end; procedure TLRTGrubber.Grub(Sender: TObject; const Instance: TPersistent; PropInfo: PPropInfo; var Content: string); var LRSWriter: TLRSObjectWriter; Path: String; begin if not Assigned(Instance) then exit; if not Assigned(PropInfo) then exit; if SysUtils.CompareText(PropInfo^.PropType^.Name,'TTRANSLATESTRING')<>0 then exit; if Writer.Driver is TLRSObjectWriter then begin LRSWriter:=TLRSObjectWriter(Writer.Driver); Path:=LRSWriter.GetStackPath; end else begin Path:=Instance.ClassName+'.'+PropInfo^.Name; end; FGrubbed.Add(Uppercase(Path)+'='+Content); //DebugLn(['TLRTGrubber.Grub "',FGrubbed[FGrubbed.Count-1],'"']); end; function TLazSourceFileManager.SaveUnitComponent(AnUnitInfo: TUnitInfo; LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult; function IsI18NEnabled(UnitOwners: TFPList): boolean; var i: Integer; APackage: TLazPackage; PkgFile: TPkgFile; begin if AnUnitInfo.IsPartOfProject then begin // a project unit Result:=AnUnitInfo.Project.EnableI18N and AnUnitInfo.Project.EnableI18NForLFM and (not AnUnitInfo.DisableI18NForLFM); exit; end; if (UnitOwners<>nil) then begin for i:=0 to UnitOwners.Count-1 do begin if TObject(UnitOwners[i]) is TLazPackage then begin // a package unit APackage:=TLazPackage(UnitOwners[i]); Result:=false; if APackage.EnableI18N and APackage.EnableI18NForLFM then begin PkgFile:=APackage.FindPkgFile(AnUnitInfo.Filename,true,true); Result:=(PkgFile<>nil) and (not PkgFile.DisableI18NForLFM); end; exit; end; end; end; // a rogue unit Result:=false; end; var ComponentSavingOk: boolean; MemStream, BinCompStream, TxtCompStream: TExtMemoryStream; DestroyDriver: Boolean; Writer: TWriter; ACaption, AText: string; CompResourceCode, LFMFilename, TestFilename: string; ADesigner: TDesigner; Grubber: TLRTGrubber; LRTFilename: String; AncestorUnit: TUnitInfo; Ancestor: TComponent; HasI18N: Boolean; UnitOwners: TFPList; LRSFilename: String; PropPath: String; ResType: TResourceType; begin Result:=mrCancel; // save lrs - lazarus resource file and lfm - lazarus form text file // Note: When there is a bug in the source, the include directive of the // resource code can not be found, therefore the LFM file should always // be saved first. // And therefore each TUnitInfo stores the resource filename (.lrs). // the lfm file is saved before the lrs file, because the IDE only needs the // lfm file to recreate the lrs file. // by VVI - now a LRT file is saved in addition to LFM and LRS // LRT file format (in present) are lines // .= LRSFilename:=''; ResType:=MainBuildBoss.GetResourceType(AnUnitInfo); LRSCode:=nil; if (AnUnitInfo.Component<>nil) then begin // stream component to resource code and to lfm file ComponentSavingOk:=true; // clean up component Result:=RemoveLooseEvents(AnUnitInfo,true); if Result<>mrOk then exit; // save designer form properties to the component FormEditor1.SaveHiddenDesignerFormProperties(AnUnitInfo.Component); if ResType=rtLRS then begin if (sfSaveToTestDir in Flags) then LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo) else LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true); end; // stream component to binary stream BinCompStream:=TExtMemoryStream.Create; if AnUnitInfo.ComponentLastBinStreamSize>0 then BinCompStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+LRSStreamChunkSize; Writer:=nil; DestroyDriver:=false; Grubber:=nil; UnitOwners:=nil; try UnitOwners:=PkgBoss.GetOwnersOfUnit(AnUnitInfo.Filename); Result:=mrOk; repeat try BinCompStream.Position:=0; Writer:=CreateLRSWriter(BinCompStream,DestroyDriver); // used to save lrt files HasI18N:=IsI18NEnabled(UnitOwners); if HasI18N then Grubber:=TLRTGrubber.Create(Writer); Writer.OnWriteMethodProperty:=@FormEditor1.WriteMethodPropertyEvent; //DebugLn(['TLazSourceFileManager.SaveUnitComponent AncestorInstance=',dbgsName(AncestorInstance)]); Writer.OnFindAncestor:=@FormEditor1.WriterFindAncestor; AncestorUnit:=AnUnitInfo.FindAncestorUnit; Ancestor:=nil; if AncestorUnit<>nil then Ancestor:=AncestorUnit.Component; //DebugLn(['TLazSourceFileManager.SaveUnitComponent Writer.WriteDescendent ARoot=',AnUnitInfo.Component,' Ancestor=',DbgSName(Ancestor)]); Writer.WriteDescendent(AnUnitInfo.Component,Ancestor); if DestroyDriver then Writer.Driver.Free; FreeAndNil(Writer); AnUnitInfo.ComponentLastBinStreamSize:=BinCompStream.Size; except on E: Exception do begin PropPath:=''; if Writer.Driver is TLRSObjectWriter then PropPath:=TLRSObjectWriter(Writer.Driver).GetStackPath; DumpExceptionBackTrace; ACaption:=lisStreamingError; AText:=Format(lisUnableToStreamT, [AnUnitInfo.ComponentName, AnUnitInfo.ComponentName]) + LineEnding + E.Message; if PropPath<>'' then AText := Atext + LineEnding + LineEnding + lisPathToInstance + LineEnding + PropPath; Result:=IDEMessageDialog(ACaption, AText, mtError, [mbAbort, mbRetry, mbIgnore]); if Result=mrAbort then exit; if Result=mrIgnore then Result:=mrOk; ComponentSavingOk:=false; end; end; until Result<>mrRetry; // create lazarus form resource code if ComponentSavingOk and (LRSFilename<>'') then begin if LRSCode=nil then begin LRSCode:=CodeToolBoss.CreateFile(LRSFilename); ComponentSavingOk:=(LRSCode<>nil); end; if ComponentSavingOk then begin // there is no bug in the source, so the resource code should be changed too MemStream:=TExtMemoryStream.Create; if AnUnitInfo.ComponentLastLRSStreamSize>0 then MemStream.Capacity:=AnUnitInfo.ComponentLastLRSStreamSize+LRSStreamChunkSize; try BinCompStream.Position:=0; BinaryToLazarusResourceCode(BinCompStream,MemStream ,'T'+AnUnitInfo.ComponentName,'FORMDATA'); AnUnitInfo.ComponentLastLRSStreamSize:=MemStream.Size; MemStream.Position:=0; SetLength(CompResourceCode,MemStream.Size); MemStream.Read(CompResourceCode[1],length(CompResourceCode)); finally MemStream.Free; end; end; if ComponentSavingOk then begin {$IFDEF IDE_DEBUG} writeln('TLazSourceFileManager.SaveFileResources E ',CompResourceCode); {$ENDIF} // replace lazarus form resource code in include file (.lrs) if not (sfSaveToTestDir in Flags) then begin // if resource name has changed, delete old resource if (AnUnitInfo.ComponentName<>AnUnitInfo.ComponentResourceName) and (AnUnitInfo.ComponentResourceName<>'') then begin CodeToolBoss.RemoveLazarusResource(LRSCode, 'T'+AnUnitInfo.ComponentResourceName); end; // add comment to resource file (if not already exists) if (not CodeToolBoss.AddLazarusResourceHeaderComment(LRSCode,LRSComment)) then begin ACaption:=lisResourceSaveError; AText:=Format(lisUnableToAddResourceHeaderCommentToResourceFile, [ LineEnding, '"', LRSCode.FileName, '"', LineEnding]); Result:=IDEMessageDialog(ACaption,AText,mtError,[mbIgnore,mbAbort]); if Result<>mrIgnore then exit; end; // add resource to resource file if (not CodeToolBoss.AddLazarusResource(LRSCode, 'T'+AnUnitInfo.ComponentName,CompResourceCode)) then begin ACaption:=lisResourceSaveError; AText:=Format(lisUnableToAddResourceTFORMDATAToResourceFileProbably, [AnUnitInfo.ComponentName, LineEnding, '"', LRSCode.FileName, '"', LineEnding] ); Result:=IDEMessageDialog(ACaption, AText, mtError, [mbIgnore, mbAbort]); if Result<>mrIgnore then exit; end else begin AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName; end; end else begin LRSCode.Source:=CompResourceCode; end; end; end; if ComponentSavingOk then begin if (not AnUnitInfo.IsVirtual) or (sfSaveToTestDir in Flags) then begin // save lfm file LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm'); if AnUnitInfo.IsVirtual then LFMFilename:=AppendPathDelim(MainBuildBoss.GetTestBuildDirectory)+LFMFilename; if LFMCode=nil then begin LFMCode:=CodeToolBoss.CreateFile(LFMFilename); if LFMCode=nil then begin Result:=IDEQuestionDialog(lisUnableToCreateFile, Format(lisUnableToCreateFile2, ['"', LFMFilename, '"']), mtWarning, [mrIgnore, lisContinueWithoutLoadingForm, mrCancel, lisCancelLoadingUnit, mrAbort, lisAbortAllLoading]); if Result<>mrIgnore then exit; end; end; if (LFMCode<>nil) then begin {$IFDEF IDE_DEBUG} writeln('TLazSourceFileManager.SaveFileResources E2 LFM=',LFMCode.Filename); {$ENDIF} if (ResType=rtRes) and (LFMCode.DiskEncoding<>EncodingUTF8) then begin // the .lfm file is used by fpcres, which only supports UTF8 without BOM DebugLn(['TLazSourceFileManager.SaveUnitComponent fixing encoding of ',LFMCode.Filename,' from ',LFMCode.DiskEncoding,' to ',EncodingUTF8]); LFMCode.DiskEncoding:=EncodingUTF8; end; Result:=mrOk; repeat try // transform binary to text TxtCompStream:=TExtMemoryStream.Create; if AnUnitInfo.ComponentLastLFMStreamSize>0 then TxtCompStream.Capacity:=AnUnitInfo.ComponentLastLFMStreamSize +LRSStreamChunkSize; try BinCompStream.Position:=0; LRSObjectBinaryToText(BinCompStream,TxtCompStream); AnUnitInfo.ComponentLastLFMStreamSize:=TxtCompStream.Size; // stream text to file TxtCompStream.Position:=0; LFMCode.LoadFromStream(TxtCompStream); Result:=SaveCodeBufferToFile(LFMCode,LFMCode.Filename,true); if not Result=mrOk then exit; Result:=mrCancel; finally TxtCompStream.Free; end; except on E: Exception do begin // added to get more feedback on issue 7009 Debugln('TLazSourceFileManager.SaveFileResources E3: ', E.Message); DumpExceptionBackTrace; ACaption:=lisStreamingError; AText:=Format( lisUnableToTransformBinaryComponentStreamOfTIntoText, [ AnUnitInfo.ComponentName, AnUnitInfo.ComponentName]) +LineEnding+E.Message; Result:=IDEMessageDialog(ACaption, AText, mtError, [mbAbort, mbRetry, mbIgnore]); if Result=mrAbort then exit; if Result=mrIgnore then Result:=mrOk; end; end; until Result<>mrRetry; end; end; end; // Now the most important file (.lfm) is saved. // Now save the secondary files // save the .lrt file containing the list of all translatable strings of // the component if ComponentSavingOk and (Grubber<>nil) and (Grubber.Grubbed.Count>0) and (not (sfSaveToTestDir in Flags)) and (not AnUnitInfo.IsVirtual) then begin LRTFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lrt'); DebugLn(['TLazSourceFileManager.SaveUnitComponent save lrt: ',LRTFilename]); Result:=SaveStringToFile(LRTFilename,Grubber.Grubbed.Text, [mbIgnore,mbAbort],AnUnitInfo.Filename); if (Result<>mrOk) and (Result<>mrIgnore) then exit; end; finally try FreeAndNil(BinCompStream); if DestroyDriver and (Writer<>nil) then Writer.Driver.Free; FreeAndNil(Writer); FreeAndNil(Grubber); FreeAndNil(UnitOwners); except on E: Exception do begin debugln('TLazSourceFileManager.SaveFileResources Error cleaning up: ',E.Message); end; end; end; end; {$IFDEF IDE_DEBUG} if ResourceCode<>nil then writeln('TLazSourceFileManager.SaveFileResources F ',ResourceCode.Modified); {$ENDIF} // save binary stream (.lrs) if LRSCode<>nil then begin if (not (sfSaveToTestDir in Flags)) then begin if (LRSCode.Modified) then begin if FilenameIsAbsolute(LRSCode.Filename) then LRSFilename:=LRSCode.Filename else if LRSFilename='' then LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true); if (LRSFilename<>'') and FilenameIsAbsolute(LRSFilename) then begin Result:=ForceDirectoryInteractive(ExtractFilePath(LRSFilename),[mbRetry]); if not Result=mrOk then exit; Result:=SaveCodeBufferToFile(LRSCode,LRSFilename); if not Result=mrOk then exit; end; end; end else begin TestFilename:=MainBuildBoss.GetTestUnitFilename(AnUnitInfo); LRSFilename:=ChangeFileExt(TestFilename,ExtractFileExt(LRSCode.Filename)); Result:=SaveCodeBufferToFile(LRSCode,LRSFilename); if not Result=mrOk then exit; end; end; // mark designer unmodified ADesigner:=FindRootDesigner(AnUnitInfo.Component) as TDesigner; if ADesigner<>nil then ADesigner.DefaultFormBoundsValid:=false; Result:=mrOk; {$IFDEF IDE_DEBUG} writeln('TLazSourceFileManager.SaveFileResources G ',LFMCode<>nil); {$ENDIF} end; function TLazSourceFileManager.RemoveLooseEvents(AnUnitInfo: TUnitInfo; OkOnCodeErrors: boolean): TModalResult; var ComponentModified: boolean; ActiveSrcEdit: TSourceEditor; ActiveUnitInfo: TUnitInfo; begin Result:=mrOk; if (AnUnitInfo.Component=nil) then exit; if not MainIDE.BeginCodeTool(ActiveSrcEdit,ActiveUnitInfo,[]) then exit; // unselect methods in ObjectInspector1 if (ObjectInspector1<>nil) and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then begin ObjectInspector1.EventGrid.ItemIndex:=-1; ObjectInspector1.FavoriteGrid.ItemIndex:=-1; end; //debugln('TLazSourceFileManager.RemoveDanglingEvents ',AnUnitInfo.Filename,' ',dbgsName(AnUnitInfo.Component)); // remove dangling methods Result:=RemoveDanglingEvents(AnUnitInfo.Component, AnUnitInfo.Source, True, ComponentModified); // update ObjectInspector1 if ComponentModified and (ObjectInspector1<>nil) and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then begin ObjectInspector1.EventGrid.RefreshPropertyValues; ObjectInspector1.FavoriteGrid.RefreshPropertyValues; end; end; function TLazSourceFileManager.RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string; var LFMCode, LRSCode: TCodeBuffer): TModalresult; var NewLFMFilename: String; OldSourceCode: String; NewSource: TCodeBuffer; NewFilePath: String; NewLRSFilePath: String; OldFilePath: String; OldLRSFilePath: String; OldFilename: String; NewLRSFilename: String; NewHighlighter: TLazSyntaxHighlighter; AmbiguousFiles: TStringList; AmbiguousText: string; i: Integer; AmbiguousFilename: String; OldUnitPath: String; OldLFMFilename: String; OldLRSFilename: String; OldPPUFilename: String; OutDir: string; Owners: TFPList; OldFileExisted: Boolean; ConvTool: TConvDelphiCodeTool; begin Project1.BeginUpdate(false); try OldFilename:=AnUnitInfo.Filename; OldFilePath:=ExtractFilePath(OldFilename); OldLFMFilename:=''; if FilenameIsPascalUnit(OldFilename) then begin OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm'); if not FileExistsUTF8(OldLFMFilename) then OldLFMFilename:=ChangeFileExt(OldFilename,'.dfm'); end; if NewUnitName='' then NewUnitName:=AnUnitInfo.Unit_Name; debugln(['TLazSourceFileManager.RenameUnit ',AnUnitInfo.Filename,' NewUnitName=',NewUnitName,' OldUnitName=',AnUnitInfo.Unit_Name,' LFMCode=',LFMCode<>nil,' LRSCode=',LRSCode<>nil,' NewFilename="',NewFilename,'"']); // check new resource file NewLFMFilename:=''; if FilenameIsPascalUnit(NewFilename) then NewLFMFilename:=ChangeFileExt(NewFilename,'.lfm'); if AnUnitInfo.ComponentName='' then begin // unit has no component // -> remove lfm file, so that it will not be auto loaded on next open if (FileExistsUTF8(NewLFMFilename)) and (not DeleteFileUTF8(NewLFMFilename)) and (IDEMessageDialog(lisPkgMangDeleteFailed, Format(lisDeletingOfFileFailed, [ '"', NewLFMFilename, '"']), mtError, [mbIgnore, mbCancel])=mrCancel) then begin Result:=mrCancel; exit; end; end; // create new source with the new filename OldSourceCode:=AnUnitInfo.Source.Source; NewSource:=CodeToolBoss.CreateFile(NewFilename); NewSource.Source:=OldSourceCode; if NewSource=nil then begin Result:=IDEMessageDialog(lisUnableToCreateFile, Format(lisCanNotCreateFile, ['"', NewFilename, '"']), mtError,[mbCancel,mbAbort]); exit; end; // get final filename NewFilename:=NewSource.Filename; NewFilePath:=ExtractFilePath(NewFilename); EnvironmentOptions.RemoveFromRecentOpenFiles(OldFilename); EnvironmentOptions.AddToRecentOpenFiles(NewFilename); MainIDE.SetRecentFilesMenu; // add new path to unit path if AnUnitInfo.IsPartOfProject and (not Project1.IsVirtual) and (FilenameIsPascalUnit(NewFilename)) and (CompareFilenames(NewFilePath,Project1.ProjectDirectory)<>0) then begin OldUnitPath:=Project1.CompilerOptions.GetUnitPath(false); if SearchDirectoryInSearchPath(OldUnitPath,NewFilePath,1)<1 then begin //DebugLn('TLazSourceFileManager.RenameUnit NewFilePath="',NewFilePath,'" OldUnitPath="',OldUnitPath,'"'); if IDEMessageDialog(lisExtendUnitPath, Format(lisTheDirectoryIsNotYetInTheUnitPathAddIt, ['"', NewFilePath, '"', LineEnding]), mtConfirmation,[mbYes,mbNo])=mrYes then begin Project1.CompilerOptions.OtherUnitFiles:= Project1.CompilerOptions.OtherUnitFiles+';' +CreateRelativePath(NewFilePath,Project1.ProjectDirectory); end; end; end; // rename lfm file if FilenameIsAbsolute(NewLFMFilename) then begin if (LFMCode=nil) and (OldLFMFilename<>'') and FilenameIsAbsolute(OldLFMFilename) and FileExistsUTF8(OldLFMFilename) then LFMCode:=CodeToolBoss.LoadFile(OldLFMFilename,false,false); if (LFMCode<>nil) then begin Result:=SaveCodeBufferToFile(LFMCode,NewLFMFilename,true); if not (Result in [mrOk,mrIgnore]) then begin DebugLn(['TLazSourceFileManager.RenameUnit SaveCodeBufferToFile failed for "',NewLFMFilename,'"']); exit; end; LFMCode:=CodeToolBoss.LoadFile(NewLFMFilename,true,false); if LFMCode<>nil then NewLFMFilename:=LFMCode.Filename; ConvTool:=TConvDelphiCodeTool.Create(NewSource); try if not ConvTool.RenameResourceDirectives then debugln(['TLazSourceFileManager.RenameUnit WARNING: unable to rename resource directive in "',NewSource.Filename,'"']); finally ConvTool.Free; end; end; end; // rename Resource file (.lrs) if (LRSCode<>nil) then begin // the resource include line in the code will be changed later after // changing the unitname if AnUnitInfo.IsPartOfProject and (not Project1.IsVirtual) and (pfLRSFilesInOutputDirectory in Project1.Flags) then begin NewLRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo); NewLRSFilename:=AppendPathDelim(ExtractFilePath(NewLRSFilename)) +ExtractFileNameOnly(NewFilename)+ResourceFileExt; end else begin OldLRSFilePath:=ExtractFilePath(LRSCode.Filename); NewLRSFilePath:=OldLRSFilePath; if FilenameIsAbsolute(OldFilePath) and FileIsInPath(OldLRSFilePath,OldFilePath) then begin // resource code was in the same or in a sub directory of source // -> try to keep this relationship NewLRSFilePath:=NewFilePath +copy(LRSCode.Filename,length(OldFilePath)+1, length(LRSCode.Filename)); if not DirPathExists(NewLRSFilePath) then NewLRSFilePath:=NewFilePath; end else begin // resource code was not in the same or in a sub directory of source // copy resource into the same directory as the source NewLRSFilePath:=NewFilePath; end; NewLRSFilename:=NewLRSFilePath +ExtractFileNameOnly(NewFilename)+ResourceFileExt; end; Result:=ForceDirectoryInteractive(ExtractFilePath(NewLRSFilename),[mbRetry,mbIgnore]); if Result=mrCancel then exit; if Result=mrOk then begin if not CodeToolBoss.SaveBufferAs(LRSCode,NewLRSFilename,LRSCode) then DebugLn(['TLazSourceFileManager.RenameUnit CodeToolBoss.SaveBufferAs failed: NewResFilename="',NewLRSFilename,'"']); end; {$IFDEF IDE_DEBUG} debugln(['TLazSourceFileManager.RenameUnit C ',ResourceCode<>nil]); debugln([' NewResFilePath="',NewResFilePath,'" NewResFilename="',NewResFilename,'"']); if ResourceCode<>nil then debugln('*** ResourceFileName ',ResourceCode.Filename); {$ENDIF} end else begin NewLRSFilename:=''; end; // rename unit name of jit class if (AnUnitInfo.Component<>nil) then FormEditor1.RenameJITComponentUnitname(AnUnitInfo.Component,NewUnitName); {$IFDEF IDE_DEBUG} if AnUnitInfo.Component<>nil then debugln('*** AnUnitInfo.Component ',dbgsName(AnUnitInfo.Component),' ClassUnitname=',GetClassUnitName(AnUnitInfo.Component.ClassType)); debugln(['TLazSourceFileManager.RenameUnit D ',ResourceCode<>nil]); {$ENDIF} // set new codebuffer in unitinfo and sourceeditor AnUnitInfo.Source:=NewSource; AnUnitInfo.ClearModifieds; for i := 0 to AnUnitInfo.EditorInfoCount -1 do if AnUnitInfo.EditorInfo[i].EditorComponent <> nil then TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).CodeBuffer := NewSource; // the code is not changed, therefore the marks are kept // change unitname in lpi and in main source file AnUnitInfo.Unit_Name:=NewUnitName; if LRSCode<>nil then begin // change resource filename in the source include directive if not CodeToolBoss.RenameMainInclude(AnUnitInfo.Source, ExtractFilename(LRSCode.Filename),false) then DebugLn(['TLazSourceFileManager.RenameUnit CodeToolBoss.RenameMainInclude failed: AnUnitInfo.Source="',AnUnitInfo.Source,'" ResourceCode="',ExtractFilename(LRSCode.Filename),'"']); end; // change unitname on SourceNotebook if AnUnitInfo.OpenEditorInfoCount > 0 then UpdateSourceNames; // change syntax highlighter NewHighlighter:=FilenameToLazSyntaxHighlighter(NewFilename); AnUnitInfo.UpdateDefaultHighlighter(NewHighlighter); for i := 0 to AnUnitInfo.EditorInfoCount - 1 do if (AnUnitInfo.EditorInfo[i].EditorComponent <> nil) and (not AnUnitInfo.EditorInfo[i].CustomHighlighter) then TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).SyntaxHighlighterType := AnUnitInfo.EditorInfo[i].SyntaxHighlighter; // save file if not NewSource.IsVirtual then begin Result:=AnUnitInfo.WriteUnitSource; if Result<>mrOk then exit; AnUnitInfo.Modified:=false; end; // change lpks containing the file Result:=PkgBoss.OnRenameFile(OldFilename,AnUnitInfo.Filename, AnUnitInfo.IsPartOfProject); if Result=mrAbort then exit; OldFileExisted:=FilenameIsAbsolute(OldFilename) and FileExistsUTF8(OldFilename); // delete ambiguous files NewFilePath:=ExtractFilePath(NewFilename); AmbiguousFiles:= FindFilesCaseInsensitive(NewFilePath,ExtractFilename(NewFilename),true); if AmbiguousFiles<>nil then begin try if (AmbiguousFiles.Count=1) and (CompareFilenames(OldFilePath,NewFilePath)=0) and (CompareFilenames(AmbiguousFiles[0],ExtractFilename(OldFilename))=0) then AmbiguousText:=Format(lisDeleteOldFile, ['"', ExtractFilename( OldFilename), '"']) else AmbiguousText:= Format(lisThereAreOtherFilesInTheDirectoryWithTheSameName, [LineEnding, LineEnding, AmbiguousFiles.Text, LineEnding]); Result:=IDEMessageDialog(lisAmbiguousFilesFound, AmbiguousText, mtWarning,[mbYes,mbNo,mbAbort]); if Result=mrAbort then exit; if Result=mrYes then begin NewFilePath:=AppendPathDelim(ExtractFilePath(NewFilename)); for i:=0 to AmbiguousFiles.Count-1 do begin AmbiguousFilename:=NewFilePath+AmbiguousFiles[i]; if (FileExistsUTF8(AmbiguousFilename)) and (not DeleteFileUTF8(AmbiguousFilename)) and (IDEMessageDialog(lisPkgMangDeleteFailed, Format(lisDeletingOfFileFailed, ['"', AmbiguousFilename, '"']), mtError, [mbIgnore, mbCancel])= mrCancel) then begin Result:=mrCancel; exit; end; end; end; finally AmbiguousFiles.Free; end; end; // remove old path from unit path if AnUnitInfo.IsPartOfProject and (FilenameIsPascalUnit(OldFilename)) and (OldFilePath<>'') then begin //DebugLn('TLazSourceFileManager.RenameUnit OldFilePath="',OldFilePath,'" SourceDirs="',Project1.SourceDirectories.CreateSearchPathFromAllFiles,'"'); if (SearchDirectoryInSearchPath( Project1.SourceDirectories.CreateSearchPathFromAllFiles,OldFilePath,1)<1) then begin //DebugLn('TLazSourceFileManager.RenameUnit OldFilePath="',OldFilePath,'" UnitPath="',Project1.CompilerOptions.GetUnitPath(false),'"'); if (SearchDirectoryInSearchPath( Project1.CompilerOptions.GetUnitPath(false),OldFilePath,1)<1) then begin if IDEMessageDialog(lisCleanUpUnitPath, Format(lisTheDirectoryIsNoLongerNeededInTheUnitPathRemoveIt, ['"', OldFilePath, '"', LineEnding]), mtConfirmation,[mbYes,mbNo])=mrYes then begin Project1.CompilerOptions.OtherUnitFiles:= RemoveSearchPaths(Project1.CompilerOptions.OtherUnitFiles, OldUnitPath); end; end; end; end; // delete old pas, .pp, .ppu if (CompareFilenames(NewFilename,OldFilename)<>0) and OldFileExisted then begin if IDEMessageDialog(lisDeleteOldFile2, Format(lisDeleteOldFile, ['"', OldFilename, '"']), mtConfirmation,[mbYes,mbNo])=mrYes then begin Result:=DeleteFileInteractive(OldFilename,[mbAbort]); if Result=mrAbort then exit; // delete old lfm //debugln(['TLazSourceFileManager.RenameUnit NewLFMFilename=',NewLFMFilename,' exists=',FileExistsUTF8(NewLFMFilename),' Old=',OldLFMFilename,' exists=',FileExistsUTF8(OldLFMFilename)]); if FileExistsUTF8(NewLFMFilename) then begin // the new file has a lfm, so it is safe to delete the old // (if NewLFMFilename does not exist, it didn't belong to the unit // or there was an error during delete. Never delete files in doubt.) OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm'); if FileExistsUTF8(OldLFMFilename) then begin Result:=DeleteFileInteractive(OldLFMFilename,[mbAbort]); if Result=mrAbort then exit; end; end; // delete old lrs if (LRSCode<>nil) and FileExistsUTF8(LRSCode.Filename) then begin // the new file has a lrs, so it is safe to delete the old // (if the new lrs does not exist, it didn't belong to the unit // or there was an error during delete. Never delete files in doubt.) OldLRSFilename:=ChangeFileExt(OldFilename,ResourceFileExt); if FileExistsUTF8(OldLRSFilename) then begin Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]); if Result=mrAbort then exit; end; end; // delete ppu in source directory OldPPUFilename:=ChangeFileExt(OldFilename,'.ppu'); if FileExistsUTF8(OldPPUFilename) then begin Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]); if Result=mrAbort then exit; end; OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o'); if FileExistsUTF8(OldPPUFilename) then begin Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]); if Result=mrAbort then exit; end; Owners:=PkgBoss.GetOwnersOfUnit(NewFilename); try if Owners<>nil then begin for i:=0 to Owners.Count-1 do begin OutDir:=''; if TObject(Owners[i]) is TProject then begin // delete old files in project output directory OutDir:=TProject(Owners[i]).CompilerOptions.GetUnitOutPath(false); end else if TObject(Owners[i]) is TLazPackage then begin // delete old files in package output directory OutDir:=TLazPackage(Owners[i]).CompilerOptions.GetUnitOutPath(false); end; if (OutDir<>'') and FilenameIsAbsolute(OutDir) then begin OldPPUFilename:=AppendPathDelim(OutDir)+ChangeFileExt(ExtractFilenameOnly(OldFilename),'.ppu'); if FileExistsUTF8(OldPPUFilename) then begin Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]); if Result=mrAbort then exit; end; OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o'); if FileExistsUTF8(OldPPUFilename) then begin Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]); if Result=mrAbort then exit; end; OldLRSFilename:=ChangeFileExt(OldPPUFilename,ResourceFileExt); if FileExistsUTF8(OldLRSFilename) then begin Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]); if Result=mrAbort then exit; end; end; end; end; finally Owners.Free; end; end; end; finally Project1.EndUpdate; end; Result:=mrOk; end; function TLazSourceFileManager.OpenNotExistingFile(const AFileName: string; Flags: TOpenFlags): TModalResult; var NewFlags: TNewFlags; begin if ofProjectLoading in Flags then begin // this is a file, that was loaded last time, but was removed from disk Result:=IDEQuestionDialog(lisFileNotFound, Format(lisTheFileWasNotFoundIgnoreWillGoOnLoadingTheProject, ['"', AFilename, '"', LineEnding, LineEnding, LineEnding]), mtError, [mrIgnore, lisSkipFileAndContinueLoading, mrAbort, lisAbortLoadingProject]); exit; end; // Default to cancel Result:=mrCancel; if ofQuiet in Flags then Exit; if ofOnlyIfExists in Flags then begin IDEMessageDialog(lisFileNotFound, Format(lisFileNotFound2, ['"', AFilename, '"', LineEnding]), mtInformation,[mbCancel]); // cancel loading file Exit; end; if IDEMessageDialog(lisFileNotFound, Format(lisFileNotFoundDoYouWantToCreateIt, ['"',AFilename,'"',LineEnding,LineEnding]), mtInformation,[mbYes,mbNo])=mrYes then begin // create new file NewFlags:=[nfOpenInEditor,nfCreateDefaultSrc]; if ofAddToProject in Flags then Include(NewFlags,nfIsPartOfProject); if FilenameIsPascalSource(AFilename) then Result:=MainIDE.DoNewEditorFile(FileDescriptorUnit,AFilename,'',NewFlags) else Result:=MainIDE.DoNewEditorFile(FileDescriptorText,AFilename,'',NewFlags); end; end; function TLazSourceFileManager.OpenUnknownFile(const AFileName: string; Flags: TOpenFlags; var NewUnitInfo: TUnitInfo; var Handled: boolean): TModalResult; var Ext, NewProgramName, LPIFilename, ACaption, AText: string; PreReadBuf: TCodeBuffer; LoadFlags: TLoadBufferFlags; SourceType: String; begin Handled:=false; Ext:=lowercase(ExtractFileExt(AFilename)); if ([ofProjectLoading,ofRegularFile]*Flags=[]) and (MainIDE.ToolStatus=itNone) and (Ext='.lpi') then begin // this is a project info file -> load whole project Result:=InitOpenedProjectFile(AFilename,[ofAddToRecent]); Handled:=true; exit; end; // load the source LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert]; if ofQuiet in Flags then Include(LoadFlags, lbfQuiet); Result:=LoadCodeBuffer(PreReadBuf,AFileName,LoadFlags,true); if Result<>mrOk then exit; NewUnitInfo:=nil; // check if unit is a program if ([ofProjectLoading,ofRegularFile]*Flags=[]) and FilenameIsPascalSource(AFilename) then begin SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false); if (SysUtils.CompareText(SourceType,'PROGRAM')=0) or (SysUtils.CompareText(SourceType,'LIBRARY')=0) then begin NewProgramName:=CodeToolBoss.GetSourceName(PreReadBuf,false); if NewProgramName<>'' then begin // source is a program // either this is a lazarus project or it is not yet a lazarus project ;) LPIFilename:=ChangeFileExt(AFilename,'.lpi'); if FileExistsUTF8(LPIFilename) then begin if IDEQuestionDialog(lisProjectInfoFileDetected, Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP, [AFilename]), mtConfirmation, [mrOk, lisOpenProject2, mrCancel, lisOpenTheFileAsNormalSource])=mrOk then begin Result:=InitOpenedProjectFile(LPIFilename,[]); Handled:=true; exit; end; end else begin AText:=Format(lisTheFileSeemsToBeAProgramCloseCurrentProject, ['"', AFilename, '"', LineEnding, LineEnding]); ACaption:=lisProgramDetected; if IDEMessageDialog(ACaption, AText, mtConfirmation, [mbYes, mbNo])=mrYes then begin Result:=CreateProjectForProgram(PreReadBuf); Handled:=true; exit; end; end; end; end; end; NewUnitInfo:=TUnitInfo.Create(PreReadBuf); if FilenameIsPascalSource(NewUnitInfo.Filename) then NewUnitInfo.ReadUnitNameFromSource(true); Project1.AddFile(NewUnitInfo,false); if (ofAddToProject in Flags) and (not NewUnitInfo.IsPartOfProject) then begin NewUnitInfo.IsPartOfProject:=true; Project1.Modified:=true; end; Result:=mrOk; end; function TLazSourceFileManager.OpenMainUnit(PageIndex, WindowIndex: integer; Flags: TOpenFlags): TModalResult; var MainUnitInfo: TUnitInfo; begin {$IFDEF IDE_VERBOSE} debugln(['[TLazSourceFileManager.DoOpenMainUnit] A ProjectLoading=',ofProjectLoading in Flags,' MainUnitID=',Project1.MainUnitID]); {$ENDIF} Result:=mrCancel; if (Project1=nil) or (Project1.MainUnitID<0) then exit; MainUnitInfo:=Project1.MainUnitInfo; // check if main unit is already open in source editor if (MainUnitInfo.OpenEditorInfoCount > 0) and (not (ofProjectLoading in Flags)) then begin // already loaded -> switch to source editor SourceEditorManager.ActiveEditor := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent); SourceEditorManager.ShowActiveWindowOnTop(True); Result:=mrOk; exit; end; // open file in source notebook Result:=OpenFileInSourceEditor(MainUnitInfo.GetClosedOrNewEditorInfo, PageIndex,WindowIndex,Flags); if Result<>mrOk then exit; Result:=mrOk; {$IFDEF IDE_VERBOSE} writeln('[TLazSourceFileManager.DoOpenMainUnit] END'); {$ENDIF} end; function TLazSourceFileManager.RevertMainUnit: TModalResult; begin Result:=mrOk; if Project1.MainUnitID<0 then exit; if Project1.MainUnitInfo.OpenEditorInfoCount > 0 then // main unit is loaded, so we can just revert Result:=OpenEditorFile('',Project1.MainUnitInfo.EditorInfo[0].PageIndex, Project1.MainUnitInfo.EditorInfo[0].WindowIndex, nil, [ofRevert]) else begin // main unit is only loaded in background // -> just reload the source and update the source name Result:=Project1.MainUnitInfo.ReadUnitSource(true,true); end; end; function TLazSourceFileManager.CheckLFMInEditor( LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult; var LFMChecker: TLFMChecker; UnitFilename: String; PascalBuf: TCodeBuffer; i: integer; begin // check, if a .lfm file is opened in the source editor if (LFMUnitInfo=nil) or ((CompareFileExt(LFMUnitInfo.Filename,'.lfm',false)<>0) and (CompareFileExt(LFMUnitInfo.Filename,'.dfm',false)<>0)) then begin if not Quiet then begin IDEMessageDialog(lisNoLFMFile, lisThisFunctionNeedsAnOpenLfmFileInTheSourceEditor, mtError,[mbCancel]); end; Result:=mrCancel; exit; end; // try to find the pascal unit for i:=Low(PascalFileExt) to High(PascalFileExt) do begin UnitFilename:=ChangeFileExt(LFMUnitInfo.Filename,PascalFileExt[i]); if FileExistsUTF8(UnitFilename) then break else UnitFilename:=''; end; if UnitFilename='' then begin IDEMessageDialog(lisNoPascalFile, Format(lisUnableToFindPascalUnitPasPpForLfmFile, [LineEnding, '"', LFMUnitInfo.Filename, '"']), mtError,[mbCancel]); Result:=mrCancel; exit; end; if MainIDE.ToolStatus<>itNone then begin DebugLn(['TLazSourceFileManager.DoCheckLFMInEditor ToolStatus<>itNone']); Result:=mrCancel; exit; end; // load the pascal unit SaveSourceEditorChangesToCodeCache(nil); Result:=LoadCodeBuffer(PascalBuf,UnitFilename,[],false); if Result<>mrOk then exit; // open messages window SourceEditorManager.ClearErrorLines; if MessagesView<>nil then MessagesView.Clear; ArrangeSourceEditorAndMessageView(false); // parse the LFM file and the pascal unit LFMChecker:=TLFMChecker.Create(PascalBuf,LFMUnitInfo.Source,@MessagesView.AddMsg); try LFMChecker.RootMustBeClassInUnit:=true; LFMChecker.RootMustBeClassInIntf:=true; LFMChecker.ObjectsMustExist:=true; if LFMChecker.Repair=mrOk then begin if not Quiet then begin IDEMessageDialog(lisLFMIsOk, lisClassesAndPropertiesExistValuesWereNotChecked, mtInformation,[mbOk],''); end; end else begin MainIDE.DoJumpToCompilerMessage(-1,true); Result:=mrAbort; exit; end; finally LFMChecker.Free; end; Result:=mrOk; end; function TLazSourceFileManager.OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo; PageIndex, WindowIndex: integer; Flags: TOpenFlags): TModalResult; var NewSrcEdit: TSourceEditor; AFilename: string; NewCaretXY: TPoint; NewTopLine: LongInt; NewLeftChar: LongInt; NewErrorLine: LongInt; NewExecutionLine: LongInt; FoldState: String; SrcNotebook: TSourceNotebook; AnUnitInfo: TUnitInfo; AShareEditor: TSourceEditor; begin //debugln(['TLazSourceFileManager.OpenFileInSourceEditor ',AnEditorInfo.UnitInfo.Filename,' Window=',WindowIndex,'/',SourceEditorManager.SourceWindowCount,' Page=',PageIndex]); AnUnitInfo := AnEditorInfo.UnitInfo; AFilename:=AnUnitInfo.Filename; if (WindowIndex < 0) then SrcNotebook := SourceEditorManager.ActiveOrNewSourceWindow else if (WindowIndex >= SourceEditorManager.SourceWindowCount) then begin SrcNotebook := SourceEditorManager.NewSourceWindow; Project1.MoveUnitWindowIndex(WindowIndex, SourceEditorManager.ActiveSourceWindowIndex); end else SrcNotebook := SourceEditorManager.SourceWindows[WindowIndex]; // get syntax highlighter type if (uifInternalFile in AnUnitInfo.Flags) then AnUnitInfo.UpdateDefaultHighlighter(lshFreePascal) else AnUnitInfo.UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(AFilename)); SrcNotebook.IncUpdateLock; try //DebugLn(['TLazSourceFileManager.OpenFileInSourceEditor Revert=',ofRevert in Flags,' ',AnUnitInfo.Filename,' PageIndex=',PageIndex]); if (not (ofRevert in Flags)) or (PageIndex<0) then begin // create a new source editor // update marks and cursor positions in Project1, so that merging the old // settings during restoration will work SaveSourceEditorProjectSpecificSettings; AShareEditor := nil; if AnUnitInfo.OpenEditorInfoCount > 0 then AShareEditor := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent); NewSrcEdit:=SrcNotebook.NewFile( CreateSrcEditPageName(AnUnitInfo.Unit_Name, AFilename, AShareEditor), AnUnitInfo.Source, False, AShareEditor); NewSrcEdit.EditorComponent.BeginUpdate; MainIDEBar.itmFileClose.Enabled:=True; MainIDEBar.itmFileCloseAll.Enabled:=True; NewCaretXY := AnEditorInfo.CursorPos; NewTopLine := AnEditorInfo.TopLine; FoldState := AnEditorInfo.FoldState; NewLeftChar:=1; NewErrorLine:=-1; NewExecutionLine:=-1; end else begin // revert code in existing source editor NewSrcEdit:=SourceEditorManager.SourceEditorsByPage[WindowIndex, PageIndex]; NewCaretXY:=NewSrcEdit.EditorComponent.CaretXY; NewTopLine:=NewSrcEdit.EditorComponent.TopLine; FoldState := NewSrcEdit.EditorComponent.FoldState; NewLeftChar:=NewSrcEdit.EditorComponent.LeftChar; NewErrorLine:=NewSrcEdit.ErrorLine; NewExecutionLine:=NewSrcEdit.ExecutionLine; NewSrcEdit.EditorComponent.BeginUpdate; if NewSrcEdit.CodeBuffer=AnUnitInfo.Source then begin AnUnitInfo.Source.AssignTo(NewSrcEdit.EditorComponent.Lines,true); end else NewSrcEdit.CodeBuffer:=AnUnitInfo.Source; AnUnitInfo.ClearModifieds; //DebugLn(['TLazSourceFileManager.OpenFileInSourceEditor NewCaretXY=',dbgs(NewCaretXY),' NewTopLine=',NewTopLine]); end; NewSrcEdit.IsLocked := AnEditorInfo.IsLocked; AnEditorInfo.EditorComponent := NewSrcEdit; //debugln(['TLazSourceFileManager.OpenFileInSourceEditor ',AnUnitInfo.Filename,' ',AnUnitInfo.EditorIndex]); // restore source editor settings DebugBoss.DoRestoreDebuggerMarks(AnUnitInfo); NewSrcEdit.SyntaxHighlighterType := AnEditorInfo.SyntaxHighlighter; NewSrcEdit.EditorComponent.AfterLoadFromFile; try NewSrcEdit.EditorComponent.FoldState := FoldState; except IDEMessageDialog(lisError, lisFailedToLoadFoldStat, mtError, [mbOK]); end; NewSrcEdit.EditorComponent.CaretXY:=NewCaretXY; NewSrcEdit.EditorComponent.TopLine:=NewTopLine; NewSrcEdit.EditorComponent.LeftChar:=NewLeftChar; NewSrcEdit.ErrorLine:=NewErrorLine; NewSrcEdit.ExecutionLine:=NewExecutionLine; NewSrcEdit.ReadOnly:=AnUnitInfo.ReadOnly; NewSrcEdit.Modified:=false; // mark unit as loaded NewSrcEdit.EditorComponent.EndUpdate; AnUnitInfo.Loaded:=true; finally SrcNotebook.DecUpdateLock; end; // update statusbar and focus editor if (not (ofProjectLoading in Flags)) then begin SourceEditorManager.ActiveEditor := NewSrcEdit; SourceEditorManager.ShowActiveWindowOnTop(True); end; SrcNoteBook.UpdateStatusBar; SrcNotebook.BringToFront; Result:=mrOk; end; function TLazSourceFileManager.LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer; IgnoreSourceErrors, AutoCreateResourceCode, ShowAbort: boolean): TModalResult; const LfmSuffices: array[0..1] of string = ('.lfm', '.dfm'); var LFMFilename: string; LRSFilename: String; ResType: TResourceType; i: Integer; begin LFMCode:=nil; LRSCode:=nil; //DebugLn(['TLazSourceFileManager.LoadResourceFile ',AnUnitInfo.Filename,' HasResources=',AnUnitInfo.HasResources,' IgnoreSourceErrors=',IgnoreSourceErrors,' AutoCreateResourceCode=',AutoCreateResourceCode]); // Load the lfm file (without parsing) if not AnUnitInfo.IsVirtual then begin // and (AnUnitInfo.Component<>nil) for i := Low(LfmSuffices) to High(LfmSuffices) do begin LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,LfmSuffices[i]); if (FileExistsUTF8(LFMFilename)) then begin Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],ShowAbort); if not (Result in [mrOk,mrIgnore]) then exit; Break; end; end; end; if AnUnitInfo.HasResources then begin //writeln('TLazSourceFileManager.LoadResourceFile A "',AnUnitInfo.Filename,'" "',AnUnitInfo.ResourceFileName,'"'); ResType:=MainBuildBoss.GetResourceType(AnUnitInfo); if ResType=rtLRS then begin LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,false); if LRSFilename<>'' then begin Result:=LoadCodeBuffer(LRSCode,LRSFilename,[lbfUpdateFromDisk],ShowAbort); if Result<>mrOk then exit; end else begin LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo); if AutoCreateResourceCode then begin LRSCode:=CodeToolBoss.CreateFile(LRSFilename); end else begin DebugLn(['TLazSourceFileManager.LoadResourceFile .lrs file not found of unit ',AnUnitInfo.Filename]); exit(mrCancel); end; end; end else begin LRSFilename:=''; LRSCode:=nil; end; // if no resource file found (i.e. normally the .lrs file) // don't bother the user, because it is created automatically anyway end; Result:=mrOk; end; function TLazSourceFileManager.LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult; // if there is a .lfm file, open the resource var LFMFilename: string; LFMBuf: TCodeBuffer; CanAbort: boolean; begin CanAbort:=[ofProjectLoading,ofMultiOpen]*OpenFlags<>[]; // Note: think about virtual and normal .lfm files. LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm'); if not FileExistsInIDE(LFMFilename,[pfsfOnlyEditorFiles]) then LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.dfm'); LFMBuf:=nil; if not FileExistsInIDE(LFMFilename,[pfsfOnlyEditorFiles]) then begin // there is no LFM file -> ok {$IFDEF IDE_DEBUG} debugln('TLazSourceFileManager.LoadLFM there is no LFM file for "',AnUnitInfo.Filename,'"'); {$ENDIF} Result:=mrOk; exit; end; // there is a lazarus form text file -> load it Result:=LoadIDECodeBuffer(LFMBuf,LFMFilename,[lbfUpdateFromDisk],CanAbort); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM LoadIDECodeBuffer failed']); exit; end; Result:=LoadLFM(AnUnitInfo,LFMBuf,OpenFlags,CloseFlags); end; function TLazSourceFileManager.LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult; const BufSize = 4096; // allocating mem in 4k chunks helps many mem managers ShowCommands: array[TWindowState] of Integer = (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN); var TxtLFMStream, BinStream: TExtMemoryStream; NewComponent: TComponent; AncestorType: TComponentClass; DesignerForm: TCustomForm; NewClassName: String; LFMType: String; ACaption, AText: String; NewUnitName: String; AncestorUnitInfo, NestedUnitInfo, LFMUnitInfo: TUnitInfo; ReferencesLocked: Boolean; LCLVersion: string; MissingClasses: TStrings; LFMComponentName: string; i: Integer; NestedClassName: string; NestedClass: TComponentClass; DisableAutoSize: Boolean; NewControl: TControl; ARestoreVisible: Boolean; begin {$IFDEF IDE_DEBUG} debugln('TLazSourceFileManager.LoadLFM A ',AnUnitInfo.Filename,' IsPartOfProject=',dbgs(AnUnitInfo.IsPartOfProject),' '); {$ENDIF} ReferencesLocked:=false; MissingClasses:=nil; NewComponent:=nil; try if (ofRevert in OpenFlags) and (AnUnitInfo.Component<>nil) then begin // the component must be destroyed and recreated => store references ReferencesLocked:=true; Project1.LockUnitComponentDependencies; Project1.UpdateUnitComponentDependencies; // close old designer form Result:=CloseUnitComponent(AnUnitInfo,CloseFlags); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM CloseUnitComponent failed']); exit; end; end; // check installed packages if EnvironmentOptions.CheckPackagesOnFormCreate and (AnUnitInfo.Component = nil) and AnUnitInfo.IsPartOfProject and (not (ofProjectLoading in OpenFlags)) then begin // opening a form of the project -> check installed packages Result := PkgBoss.CheckProjectHasInstalledPackages(Project1, OpenFlags * [ofProjectLoading, ofQuiet] = []); if not (Result in [mrOk, mrIgnore]) then begin DebugLn(['TLazSourceFileManager.LoadLFM PkgBoss.CheckProjectHasInstalledPackages failed']); exit; end; end; //debugln('TLazSourceFileManager.LoadLFM LFM file loaded, parsing "',LFMBuf.Filename,'" ...'); // someone created a .lfm file -> Update HasResources AnUnitInfo.HasResources:=true; //debugln('TLazSourceFileManager.LoadLFM LFM="',LFMBuf.Source,'"'); if AnUnitInfo.Component=nil then begin // load/create new instance // find the classname of the LFM, and check for inherited form QuickCheckLFMBuffer(AnUnitInfo.Source,LFMBuf,LFMType,LFMComponentName, NewClassName,LCLVersion,MissingClasses); if (NewClassName='') or (LFMType='') then begin DebugLn(['TLazSourceFileManager.LoadLFM LFM file corrupt']); Result:=IDEMessageDialog(lisLFMFileCorrupt, Format(lisUnableToFindAValidClassnameIn, ['"', LFMBuf.Filename, '"']), mtError,[mbIgnore,mbCancel,mbAbort]); exit; end; // load missing component classes (e.g. ancestor and frames) Result:=LoadAncestorDependencyHidden(AnUnitInfo,NewClassName,OpenFlags, AncestorType,AncestorUnitInfo); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM DoLoadAncestorDependencyHidden failed for ',AnUnitInfo.Filename]); exit; end; if MissingClasses<>nil then begin //DebugLn(['TLazSourceFileManager.LoadLFM has nested: ',AnUnitInfo.Filename]); for i:=MissingClasses.Count-1 downto 0 do begin NestedClassName:=MissingClasses[i]; //DebugLn(['TLazSourceFileManager.LoadLFM nested ',i,' ',MissingClasses.Count,': ',NestedClassName]); if SysUtils.CompareText(NestedClassName,AncestorType.ClassName)=0 then begin MissingClasses.Delete(i); end else begin DebugLn(['TLazSourceFileManager.LoadLFM loading nested class ',NestedClassName,' needed by ',AnUnitInfo.Filename]); NestedClass:=nil; NestedUnitInfo:=nil; Result:=LoadComponentDependencyHidden(AnUnitInfo,NestedClassName, OpenFlags,true,NestedClass,NestedUnitInfo); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM DoLoadComponentDependencyHidden NestedClassName=',NestedClassName,' failed for ',AnUnitInfo.Filename]); exit; end; end; end; //DebugLn(['TLazSourceFileManager.LoadLFM had nested: ',AnUnitInfo.Filename]); end; BinStream:=nil; try // convert text to binary format BinStream:=TExtMemoryStream.Create; TxtLFMStream:=TExtMemoryStream.Create; try {$IFDEF VerboseIDELFMConversion} DebugLn(['TLazSourceFileManager.LoadLFM LFMBuf START =======================================']); DebugLn(LFMBuf.Source); DebugLn(['TLazSourceFileManager.LoadLFM LFMBuf END =======================================']); {$ENDIF} LFMBuf.SaveToStream(TxtLFMStream); AnUnitInfo.ComponentLastLFMStreamSize:=TxtLFMStream.Size; TxtLFMStream.Position:=0; try if AnUnitInfo.ComponentLastBinStreamSize>0 then BinStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+BufSize; LRSObjectTextToBinary(TxtLFMStream,BinStream); AnUnitInfo.ComponentLastBinStreamSize:=BinStream.Size; BinStream.Position:=0; {$IFDEF VerboseIDELFMConversion} DebugLn(['TLazSourceFileManager.LoadLFM Binary START =======================================']); debugln(dbgMemStream(BinStream,BinStream.Size)); DebugLn(['TLazSourceFileManager.LoadLFM Binary END =======================================']); BinStream.Position:=0; {$ENDIF} Result:=mrOk; except on E: Exception do begin DumpExceptionBackTrace; ACaption:=lisFormatError; AText:=Format(lisUnableToConvertTextFormDataOfFileIntoBinaryStream, [LineEnding, '"', LFMBuf.Filename, '"', LineEnding, E.Message]); Result:=IDEMessageDialog(ACaption, AText, mtError, [mbOk, mbCancel]); if Result=mrCancel then Result:=mrAbort; exit; end; end; finally TxtLFMStream.Free; end; if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then FormEditor1.ClearSelection; // create JIT component NewUnitName:=AnUnitInfo.Unit_Name; if NewUnitName='' then NewUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename); // ToDo: create AncestorBinStream(s) via hook, not via parameters DisableAutoSize:=true; NewComponent:=FormEditor1.CreateRawComponentFromStream(BinStream, AncestorType,copy(NewUnitName,1,255),true,true,DisableAutoSize,AnUnitInfo); if (NewComponent is TControl) then begin NewControl:=TControl(NewComponent); if ofLoadHiddenResource in OpenFlags then NewControl.ControlStyle:=NewControl.ControlStyle+[csNoDesignVisible]; if DisableAutoSize then NewControl.EnableAutoSizing; if NewComponent is TFrame then AnUnitInfo.ResourceBaseClass:=pfcbcFrame else if NewComponent is TDataModule then AnUnitInfo.ResourceBaseClass:=pfcbcDataModule else if NewComponent is TForm then AnUnitInfo.ResourceBaseClass:=pfcbcForm; end; Project1.InvalidateUnitComponentDesignerDependencies; AnUnitInfo.Component:=NewComponent; if (AncestorUnitInfo<>nil) then AnUnitInfo.AddRequiresComponentDependency(AncestorUnitInfo,[ucdtAncestor]); if NewComponent<>nil then begin // component loaded, now load the referenced units Result:=MainIDE.DoFixupComponentReferences(AnUnitInfo.Component,OpenFlags); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM DoFixupComponentReferences failed']); exit; end; end else begin // error streaming component -> examine lfm file DebugLn('ERROR: streaming failed lfm="',LFMBuf.Filename,'"'); // open lfm file in editor if AnUnitInfo.OpenEditorInfoCount > 0 then Result:=OpenEditorFile(LFMBuf.Filename, AnUnitInfo.OpenEditorInfo[0].PageIndex+1, AnUnitInfo.OpenEditorInfo[0].WindowIndex, Nil, OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile]) else Result:=OpenEditorFile(LFMBuf.Filename, -1, -1, nil, OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile]); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadLFM DoOpenEditorFile failed']); exit; end; LFMUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor); Result:=CheckLFMInEditor(LFMUnitInfo, true); if Result=mrOk then Result:=mrCancel; exit; end; finally BinStream.Free; end; end else begin // keep old instance, just add a designer DebugLn(['TLazSourceFileManager.LoadLFM Creating designer for hidden component of ',AnUnitInfo.Filename]); end; finally MissingClasses.Free; if ReferencesLocked then begin if Project1<>nil then Project1.UnlockUnitComponentDependencies; end; end; NewComponent:=AnUnitInfo.Component; // create the designer (if not already done) if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then FormEditor1.ClearSelection; {$IFDEF IDE_DEBUG} DebugLn('SUCCESS: streaming lfm="',LFMBuf.Filename,'"'); {$ENDIF} AnUnitInfo.ComponentName:=NewComponent.Name; AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName; DesignerForm := nil; MainIDE.LastFormActivated := nil; if not (ofLoadHiddenResource in OpenFlags) then begin DesignerForm := FormEditor1.GetDesignerForm(NewComponent); if (DesignerForm=nil) or (DesignerForm.Designer=nil) then DesignerForm := MainIDE.CreateDesignerForComponent(AnUnitInfo,NewComponent); end; // select the new form (object inspector, formeditor, control selection) if (DesignerForm <> nil) and ([ofProjectLoading,ofLoadHiddenResource] * OpenFlags=[]) then begin MainIDE.DisplayState := dsForm; GlobalDesignHook.LookupRoot := NewComponent; TheControlSelection.AssignPersistent(NewComponent); MainIDE.CreateObjectInspector; end; // show new form if DesignerForm <> nil then begin DesignerForm.ControlStyle := DesignerForm.ControlStyle - [csNoDesignVisible]; if NewComponent is TControl then TControl(NewComponent).ControlStyle:= TControl(NewComponent).ControlStyle - [csNoDesignVisible]; if (DesignerForm.WindowState in [wsMinimized]) then begin ARestoreVisible := DesignerForm.Visible; DesignerForm.Visible := False; DesignerForm.ShowOnTop; DesignerForm.Visible := ARestoreVisible; DesignerForm.WindowState := wsMinimized; end else LCLIntf.ShowWindow(DesignerForm.Handle, ShowCommands[AnUnitInfo.ComponentState]); MainIDE.LastFormActivated := DesignerForm; end; {$IFDEF IDE_DEBUG} debugln('[TLazSourceFileManager.LoadLFM] LFM end'); {$ENDIF} Result:=mrOk; end; function TLazSourceFileManager.OpenComponent(const UnitFilename: string; OpenFlags: TOpenFlags; CloseFlags: TCloseFlags; out Component: TComponent): TModalResult; var AnUnitInfo: TUnitInfo; LFMFilename: String; UnitCode: TCodeBuffer; LFMCode: TCodeBuffer; AFilename: String; begin if Project1=nil then exit(mrCancel); // try to find a unit name without expaning the path. this is required if unit is virtual // in other case file name will be expanded with the wrong path AFilename := UnitFilename; AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename); if AnUnitInfo = nil then begin AFilename:=TrimAndExpandFilename(UnitFilename); if (AFilename='') or (not FileExistsInIDE(AFilename,[])) then begin DebugLn(['TLazSourceFileManager.DoOpenComponent file not found ',AFilename]); exit(mrCancel); end; AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename); end; if (not (ofRevert in OpenFlags)) and (AnUnitInfo<>nil) and (AnUnitInfo.Component<>nil) then begin // already open Component:=AnUnitInfo.Component; Result:=mrOk; exit; end; LFMFilename:=ChangeFileExt(AFilename,'.lfm'); if not FileExistsInIDE(LFMFilename,[]) then LFMFilename:=ChangeFileExt(AFilename,'.dfm'); if not FileExistsInIDE(LFMFilename,[]) then begin DebugLn(['TLazSourceFileManager.DoOpenComponent file not found ',LFMFilename]); exit(mrCancel); end; // load unit source Result:=LoadCodeBuffer(UnitCode,AFilename,[lbfCheckIfText],true); if Result<>mrOk then begin debugln('TLazSourceFileManager.DoOpenComponent Failed loading ',AFilename); exit; end; // create unit info if AnUnitInfo=nil then begin AnUnitInfo:=TUnitInfo.Create(UnitCode); AnUnitInfo.ReadUnitNameFromSource(true); Project1.AddFile(AnUnitInfo,false); end; // load lfm source Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true); if Result<>mrOk then begin debugln('TLazSourceFileManager.DoOpenComponent Failed loading ',LFMFilename); exit; end; // load resource Result:=LoadLFM(AnUnitInfo,LFMCode,OpenFlags,CloseFlags); if Result<>mrOk then begin debugln('TLazSourceFileManager.DoOpenComponent DoLoadLFM failed ',LFMFilename); exit; end; Component:=AnUnitInfo.Component; if Component<>nil then Result:=mrOk else Result:=mrCancel; end; function TLazSourceFileManager.FindBaseComponentClass(const AComponentClassName, DescendantClassName: string; out AComponentClass: TComponentClass): boolean; // returns false if an error occured // Important: returns true even if AComponentClass=nil begin // find the ancestor class if AComponentClassName<>'' then begin if (DescendantClassName<>'') and (SysUtils.CompareText(AComponentClassName,'TCustomForm')=0) then begin // this is a common user mistake IDEMessageDialog(lisCodeTemplError, Format( lisTheResourceClassDescendsFromProbablyThisIsATypoFor, ['"', DescendantClassName, '"', '"', AComponentClassName, '"']), mtError,[mbCancel]); Result:=false; exit; end else if (DescendantClassName<>'') and (SysUtils.CompareText(AComponentClassName,'TComponent')=0) then begin // this is not yet implemented IDEMessageDialog(lisCodeTemplError, Format(lisUnableToOpenDesignerTheClassDoesNotDescendFromADes, [LineEnding, DescendantClassName]), mtError,[mbCancel]); Result:=false; exit; end else begin // search in the registered base classes AComponentClass:=FormEditor1.FindDesignerBaseClassByName(AComponentClassName,true); end; end else begin // default is TForm AComponentClass:=TForm; end; Result:=true; end; function TLazSourceFileManager.LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo; const DescendantClassName: string; OpenFlags: TOpenFlags; out AncestorClass: TComponentClass; out AncestorUnitInfo: TUnitInfo): TModalResult; var AncestorClassName: String; CodeBuf: TCodeBuffer; begin AncestorClassName:=''; AncestorClass:=nil; AncestorUnitInfo:=nil; // find the ancestor type in the source if AnUnitInfo.Source=nil then begin Result:=LoadCodeBuffer(CodeBuf,AnUnitInfo.Filename, [lbfUpdateFromDisk,lbfCheckIfText],true); if Result<>mrOk then exit; AnUnitInfo.Source:=CodeBuf; end; if not CodeToolBoss.FindFormAncestor(AnUnitInfo.Source,DescendantClassName, AncestorClassName,true) then begin DebugLn('TLazSourceFileManager.LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" ClassName=',DescendantClassName,'. Unable to find ancestor class: ',CodeToolBoss.ErrorMessage); end; // try the base designer classes if not FindBaseComponentClass(AncestorClassName,DescendantClassName, AncestorClass) then begin DebugLn(['TLazSourceFileManager.LoadAncestorDependencyHidden FindUnitComponentClass failed for AncestorClassName=',AncestorClassName]); exit(mrCancel); end; // try loading the ancestor first (unit, lfm and component instance) if (AncestorClass=nil) then begin Result:=LoadComponentDependencyHidden(AnUnitInfo,AncestorClassName, OpenFlags,false,AncestorClass,AncestorUnitInfo); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.LoadAncestorDependencyHidden DoLoadComponentDependencyHidden failed AnUnitInfo=',AnUnitInfo.Filename]); end; case Result of mrAbort: exit; mrOk: ; mrIgnore: begin // use TForm as default AncestorClass:=TForm; AncestorUnitInfo:=nil; end; else // cancel Result:=mrCancel; exit; end; end; // use TForm as default ancestor if AncestorClass=nil then AncestorClass:=TForm; //DebugLn('TLazSourceFileManager.LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" AncestorClassName=',AncestorClassName,' AncestorClass=',dbgsName(AncestorClass)); Result:=mrOk; end; function TLazSourceFileManager.LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo; const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean; var AComponentClass: TComponentClass; var ComponentUnitInfo: TUnitInfo ): TModalResult; var CTErrorMsg: string; CTErrorCode: TCodeBuffer; CTErrorLine: LongInt; CTErrorCol: LongInt; function FindClassInUnit(UnitCode: TCodeBuffer; out TheModalResult: TModalResult; var LFMCode: TCodeBuffer; var ClassFound: boolean): boolean; var AncestorClassName: String; UsedFilename: String; UsingFilename: String; LFMFilename: String; AComponentName: String; begin Result:=false; TheModalResult:=mrCancel; LFMCode:=nil; ClassFound:=false; AncestorClassName:=''; if not CodeToolBoss.FindFormAncestor(UnitCode,AComponentClassName, AncestorClassName,true) then begin if CodeToolBoss.ErrorMessage<>'' then begin CTErrorMsg:=CodeToolBoss.ErrorMessage; CTErrorCode:=CodeToolBoss.ErrorCode; CTErrorLine:=CodeToolBoss.ErrorLine; CTErrorCol:=CodeToolBoss.ErrorColumn; end; exit; end; // this unit contains the class ClassFound:=true; LFMFilename:=ChangeFileExt(UnitCode.Filename,'.lfm'); if FileExistsUTF8(LFMFilename) then begin UsingFilename:=AnUnitInfo.Filename; Project1.ConvertToLPIFilename(UsingFilename); UsedFilename:=UnitCode.Filename; Project1.ConvertToLPIFilename(UsedFilename); TheModalResult:=IDEQuestionDialog(lisCodeTemplError, Format(lisClassConflictsWithLfmFileTheUnitUsesTheUnitWhic, [LineEnding, UsingFilename, LineEnding, UsedFilename, LineEnding, AComponentClassName, LineEnding, LineEnding, LineEnding, AComponentClassName]), mtError, [mrCancel, lisCancelLoadingThisComponent, mrAbort, lisAbortWholeLoading, mrIgnore, lisIgnoreUseTFormAsAncestor]); exit; end; // there is no .lfm file // create a dummy lfm file LFMCode:=CodeToolBoss.CreateFile(LFMFilename); if LFMCode=nil then begin debugln('TLazSourceFileManager.LoadComponentDependencyHidden Failed creating dummy lfm ',LFMFilename); exit; end; AComponentName:=AComponentClassName; if AComponentName[1] in ['T','t'] then AComponentName:=copy(AComponentName,2,length(AComponentName)); LFMCode.Source:= 'inherited '+AComponentName+': '+AComponentClassName+LineEnding +'end'; Result:=true; TheModalResult:=mrOk; end; function TryUnit(const UnitFilename: string; out TheModalResult: TModalResult; TryWithoutLFM: boolean): boolean; // returns true if the unit contains the component class and sets // TheModalResult to the result of the loading var LFMFilename: String; LFMCode: TCodeBuffer; LFMClassName: string; LFMType: string; CurUnitInfo: TUnitInfo; UnitCode: TCodeBuffer; begin Result:=false; TheModalResult:=mrCancel; if not FilenameIsPascalUnit(UnitFilename) then exit; CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename); if (CurUnitInfo<>nil) and (CurUnitInfo.Component<>nil) then begin // unit with loaded component found -> check if it is the right one //DebugLn(['TLazSourceFileManager.LoadComponentDependencyHidden unit with a component found CurUnitInfo=',CurUnitInfo.Filename,' ',dbgsName(CurUnitInfo.Component)]); if SysUtils.CompareText(CurUnitInfo.Component.ClassName,AComponentClassName)=0 then begin // component found (it was already loaded) ComponentUnitInfo:=CurUnitInfo; AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType); Result:=true; TheModalResult:=mrOk; end else begin // this unit does not have this component end; exit; end; if not TryWithoutLFM then begin LFMFilename:=ChangeFileExt(UnitFilename,'.lfm'); if not FileExistsUTF8(LFMFilename) then LFMFilename:=ChangeFileExt(UnitFilename,'.dfm'); if FileExistsUTF8(LFMFilename) then begin // load the lfm file TheModalResult:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true); if TheModalResult<>mrOk then begin debugln('TLazSourceFileManager.LoadComponentDependencyHidden Failed loading ',LFMFilename); exit; end; // read the LFM classname ReadLFMHeader(LFMCode.Source,LFMClassName,LFMType); if LFMType='' then ; if SysUtils.CompareText(LFMClassName,AComponentClassName)<>0 then exit; // .lfm found Result:=true; end else if not TryWithoutLFM then begin // unit has no .lfm exit; end; end; {$ifdef VerboseFormEditor} debugln('TLazSourceFileManager.LoadComponentDependencyHidden ',AnUnitInfo.Filename,' Loading referenced form ',UnitFilename); {$endif} // load unit source TheModalResult:=LoadCodeBuffer(UnitCode,UnitFilename,[lbfCheckIfText],true); if TheModalResult<>mrOk then begin debugln('TLazSourceFileManager.LoadComponentDependencyHidden Failed loading ',UnitFilename); exit; end; if TryWithoutLFM then begin if not FindClassInUnit(UnitCode,TheModalResult,LFMCode,Result) then exit; end; // create unit info if CurUnitInfo=nil then begin CurUnitInfo:=TUnitInfo.Create(UnitCode); CurUnitInfo.ReadUnitNameFromSource(true); Project1.AddFile(CurUnitInfo,false); end; // load resource hidden TheModalResult:=LoadLFM(CurUnitInfo,LFMCode, Flags+[ofLoadHiddenResource],[]); if (TheModalResult=mrOk) then begin ComponentUnitInfo:=CurUnitInfo; AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType); {$ifdef VerboseFormEditor} debugln('TLazSourceFileManager.LoadComponentDependencyHidden Wanted=',AComponentClassName,' Class=',AComponentClass.ClassName); {$endif} TheModalResult:=mrOk; end else begin debugln('TLazSourceFileManager.LoadComponentDependencyHidden Failed to load component ',AComponentClassName); if TheModalResult<>mrAbort then TheModalResult:=mrCancel; end; end; function TryRegisteredClasses(out TheModalResult: TModalResult): boolean; begin Result:=false; AComponentClass:= FormEditor1.FindDesignerBaseClassByName(AComponentClassName,true); if AComponentClass<>nil then begin DebugLn(['TLazSourceFileManager.LoadComponentDependencyHidden.TryRegisteredClasses found: ',AComponentClass.ClassName]); TheModalResult:=mrOk; Result:=true; end; end; var UsedUnitFilenames: TStrings; i: Integer; begin Result:=mrCancel; CTErrorMsg:=''; CTErrorCode:=nil; CTErrorLine:=0; CTErrorCol:=0; if (AComponentClassName='') or (not IsValidIdent(AComponentClassName)) then begin DebugLn(['TLazSourceFileManager.LoadComponentDependencyHidden invalid component class name "',AComponentClassName,'"']); exit(mrCancel); end; // check for circles if AnUnitInfo.LoadingComponent then begin Result:=IDEQuestionDialog(lisCodeTemplError, Format( lisUnableToLoadTheComponentClassBecauseItDependsOnIts, ['"', AComponentClassName, '"']), mtError, [mrCancel, lisCancelLoadingThisComponent, mrAbort, lisAbortWholeLoading]); exit; end; AnUnitInfo.LoadingComponent:=true; try // search component lfm {$ifdef VerboseFormEditor} debugln('TLazSourceFileManager.LoadComponentDependencyHidden ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName,' AComponentClass=',dbgsName(AComponentClass)); {$endif} // first search the resource of ComponentUnitInfo if ComponentUnitInfo<>nil then begin if TryUnit(ComponentUnitInfo.Filename,Result,false) then exit; end; // then try registered global classes if TryRegisteredClasses(Result) then exit; // finally search in used units UsedUnitFilenames:=nil; try if not CodeToolBoss.FindUsedUnitFiles(AnUnitInfo.Source,UsedUnitFilenames) then begin MainIDE.DoJumpToCodeToolBossError; Result:=mrCancel; exit; end; if (UsedUnitFilenames<>nil) then begin // search for every used unit the .lfm file for i:=UsedUnitFilenames.Count-1 downto 0 do begin if TryUnit(UsedUnitFilenames[i],Result,false) then exit; end; // search in every used unit the class if not MustHaveLFM then for i:=UsedUnitFilenames.Count-1 downto 0 do begin if TryUnit(UsedUnitFilenames[i],Result,true) then exit; end; if CTErrorMsg<>'' then begin // class not found and there was a parser error // maybe that's the reason, why the class was not found // show the user if ([ofProjectLoading,ofQuiet]*Flags=[]) then begin CodeToolBoss.SetError(CTErrorCode,CTErrorLine,CTErrorCol,CTErrorMsg); MainIDE.DoJumpToCodeToolBossError; Result:=mrAbort; exit; end; end; end; finally UsedUnitFilenames.Free; end; // not found => tell the user Result:=IDEQuestionDialog(lisCodeTemplError, Format(lisUnableToFindTheComponentClassItIsNotRegisteredViaR, [ AComponentClassName, LineEnding, LineEnding, LineEnding, AnUnitInfo.Filename]), mtError, [mrCancel, lisCancelLoadingThisComponent, mrAbort, lisAbortWholeLoading, mrIgnore, lisIgnoreUseTFormAsAncestor]); finally AnUnitInfo.LoadingComponent:=false; end; end; function TLazSourceFileManager.LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer; const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult; begin if (Project1<>nil) and (Project1.UnitInfoWithFilename(AFilename,[pfsfOnlyEditorFiles])<>nil) then Exclude(Flags,lbfUpdateFromDisk); Result:=LoadCodeBuffer(ACodeBuffer,AFilename,Flags,ShowAbort); end; function TLazSourceFileManager.CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult; var OldDesigner: TDesigner; procedure FreeDesigner(AFreeComponent: boolean); begin AnUnitInfo.LoadedDesigner:=false; OldDesigner.PrepareFreeDesigner(AFreeComponent); OldDesigner.FinalizeFreeDesigner; end; procedure FreeUnusedComponents; var CompUnitInfo: TUnitInfo; begin CompUnitInfo:=Project1.FirstUnitWithComponent; Project1.UpdateUnitComponentDependencies; while CompUnitInfo<>nil do begin //DebugLn(['FreeUnusedComponents ',CompUnitInfo.Filename,' ',dbgsName(CompUnitInfo.Component),' UnitComponentIsUsed=',UnitComponentIsUsed(CompUnitInfo,true)]); if not UnitComponentIsUsed(CompUnitInfo,true) then begin // close the unit component CloseUnitComponent(CompUnitInfo,Flags); // this has recursively freed all components, so exit here exit; end; CompUnitInfo:=CompUnitInfo.NextUnitWithComponent; end; end; var AForm: TCustomForm; LookupRoot: TComponent; ComponentStillUsed: Boolean; begin LookupRoot:=AnUnitInfo.Component; if LookupRoot=nil then exit(mrOk); {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]); {$ENDIF} Project1.LockUnitComponentDependencies; // avoid circles try // save if (cfSaveFirst in Flags) and (AnUnitInfo.OpenEditorInfoCount > 0) and (not AnUnitInfo.IsReverting) then begin Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent,[sfCheckAmbiguousFiles]); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.CloseUnitComponent DoSaveEditorFile failed']); exit; end; end; // close dependencies if cfCloseDependencies in Flags then begin {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent cfCloseDependencies ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]); {$ENDIF} Result:=CloseDependingUnitComponents(AnUnitInfo,Flags); if Result<>mrOk then begin DebugLn(['TLazSourceFileManager.CloseUnitComponent CloseDependingUnitComponents failed']); exit; end; // now only soft dependencies are left. The component can be freed. end; AForm:=FormEditor1.GetDesignerForm(LookupRoot); OldDesigner:=nil; if AForm<>nil then OldDesigner:=TDesigner(AForm.Designer); if MainIDE.LastFormActivated=AForm then MainIDE.LastFormActivated:=nil; ComponentStillUsed:=(not (cfCloseDependencies in Flags)) and UnitComponentIsUsed(AnUnitInfo,false); {$IFDEF VerboseTFrame} DebugLn(['TLazSourceFileManager.CloseUnitComponent ',AnUnitInfo.Filename,' ComponentStillUsed=',ComponentStillUsed,' UnitComponentIsUsed=',UnitComponentIsUsed(AnUnitInfo,false),' ',dbgs(AnUnitInfo.Flags),' DepAncestor=',AnUnitInfo.FindUsedByComponentDependency([ucdtAncestor])<>nil,' DepInline=',AnUnitInfo.FindUsedByComponentDependency([ucdtInlineClass])<>nil]); {$ENDIF} if (OldDesigner=nil) then begin // hidden component //DebugLn(['TLazSourceFileManager.CloseUnitComponent freeing hidden component without designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]); if ComponentStillUsed then begin // hidden component is still used => keep it {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent hidden component is still used => keep it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]); {$ENDIF} end else begin // hidden component is not used => free it {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent hidden component is not used => free it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]); {$ENDIF} try FormEditor1.DeleteComponent(LookupRoot,true); finally AnUnitInfo.Component:=nil; end; FreeUnusedComponents; end; end else begin // component with designer if ComponentStillUsed then begin // free designer, keep component hidden {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent hiding component and freeing designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]); {$ENDIF} FreeDesigner(false); end else begin // free designer and design form {$IFDEF VerboseIDEMultiForm} DebugLn(['TLazSourceFileManager.CloseUnitComponent freeing component and designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]); {$ENDIF} try FreeDesigner(true); finally AnUnitInfo.Component:=nil; end; end; Project1.InvalidateUnitComponentDesignerDependencies; FreeUnusedComponents; end; finally Project1.UnlockUnitComponentDependencies; end; Result:=mrOk; end; function TLazSourceFileManager.CloseDependingUnitComponents(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult; var UserAsked: Boolean; function CloseNext(var ModResult: TModalresult; Types: TUnitCompDependencyTypes): boolean; var DependingUnitInfo: TUnitInfo; DependenciesFlags: TCloseFlags; begin repeat DependingUnitInfo:=Project1.UnitUsingComponentUnit(AnUnitInfo,Types); if DependingUnitInfo=nil then break; if (not UserAsked) and (not (cfQuiet in Flags)) and (not DependingUnitInfo.IsReverting) then begin // ToDo: collect in advance all components to close and show user the list ModResult:=IDEQuestionDialog('Close component?', 'Close component '+dbgsName(DependingUnitInfo.Component)+'?', mtConfirmation,[mrYes,mrAbort]); if ModResult<>mrYes then exit(false); UserAsked:=true; end; // close recursively DependenciesFlags:=Flags+[cfCloseDependencies]; if cfSaveDependencies in Flags then Include(DependenciesFlags,cfSaveFirst); ModResult:=CloseUnitComponent(DependingUnitInfo,DependenciesFlags); if ModResult<>mrOk then exit(false); until false; Result:=true; end; begin UserAsked:=false; Project1.LockUnitComponentDependencies; try // Important: // This function is called recursively. // It is important that first the hard, non cyclic dependencies // are freed in the correct order. // After that the soft, cyclic dependencies can be freed in any order. // first close all descendants recursively // This must happen in the right order (descendants before ancestor) if not CloseNext(Result,[ucdtAncestor]) then exit; // then close all nested descendants recursively // This must happen in the right order (nested descendants before ancestor) if not CloseNext(Result,[ucdtInlineClass]) then exit; // then close all referring components // These can build circles and can be freed in any order. if not CloseNext(Result,[ucdtProperty]) then exit; finally Project1.UnlockUnitComponentDependencies; end; Result:=mrOk; end; function TLazSourceFileManager.UnitComponentIsUsed(AnUnitInfo: TUnitInfo; CheckHasDesigner: boolean): boolean; // if CheckHasDesigner=true and AnUnitInfo has a designer (visible) return true // otherwise check if another unit needs AnUnitInfo var LookupRoot: TComponent; begin Result:=false; LookupRoot:=AnUnitInfo.Component; if LookupRoot=nil then exit; // check if a designer or another component uses this component Project1.UpdateUnitComponentDependencies; if Project1.UnitComponentIsUsed(AnUnitInfo,CheckHasDesigner) then exit(true); //DebugLn(['TLazSourceFileManager.UnitComponentIsUsed ',AnUnitInfo.Filename,' ',dbgs(AnUnitInfo.Flags)]); end; function TLazSourceFileManager.RemoveFilesFromProject(AProject: TProject; UnitInfos: TFPList): TModalResult; var i: Integer; AnUnitInfo: TUnitInfo; ShortUnitName: String; Dummy: Boolean; ObsoleteUnitPaths: String; ObsoleteIncPaths: String; p: Integer; ProjUnitPaths: String; CurDir: String; ResolvedDir: String; OldP: LongInt; ProjIncPaths: String; begin Result:=mrOk; if UnitInfos=nil then exit; // check if something will change i:=UnitInfos.Count-1; while (i>=0) and (not TUnitInfo(UnitInfos[i]).IsPartOfProject) do dec(i); if i<0 then exit(mrOk); // check ToolStatus if (MainIDE.ToolStatus in [itCodeTools,itCodeToolAborting]) then begin debugln('TLazSourceFileManager.RemoveUnitsFromProject wrong ToolStatus ',dbgs(ord(MainIDE.ToolStatus))); exit; end; // commit changes from source editor to codetools SaveSourceEditorChangesToCodeCache(nil); ObsoleteUnitPaths:=''; ObsoleteIncPaths:=''; AProject.BeginUpdate(true); try for i:=0 to UnitInfos.Count-1 do begin AnUnitInfo:=TUnitInfo(UnitInfos[i]); //debugln(['TLazSourceFileManager.RemoveUnitsFromProject Unit ',AnUnitInfo.Filename]); if not AnUnitInfo.IsPartOfProject then continue; AnUnitInfo.IsPartOfProject:=false; AProject.Modified:=true; if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin if FilenameIsAbsolute(AnUnitInfo.Filename) then ObsoleteUnitPaths:=MergeSearchPaths(ObsoleteUnitPaths, ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename))); // remove from project's unit section if (AProject.MainUnitID>=0) and (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags) then begin ShortUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename); //debugln(['TLazSourceFileManager.RemoveUnitsFromProject UnitName=',ShortUnitName]); if (ShortUnitName<>'') then begin Dummy:=CodeToolBoss.RemoveUnitFromAllUsesSections( AProject.MainUnitInfo.Source,ShortUnitName); if not Dummy then begin MainIDE.DoJumpToCodeToolBossError; Result:=mrCancel; exit; end; end; end; // remove CreateForm statement from project if (AProject.MainUnitID>=0) and (pfMainUnitHasCreateFormStatements in AProject.Flags) and (AnUnitInfo.ComponentName<>'') then begin Dummy:=AProject.RemoveCreateFormFromProjectFile( 'T'+AnUnitInfo.ComponentName,AnUnitInfo.ComponentName); if not Dummy then begin MainIDE.DoJumpToCodeToolBossError; Result:=mrCancel; exit; end; end; end; if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then begin // include file if FilenameIsAbsolute(AnUnitInfo.Filename) then ObsoleteIncPaths:=MergeSearchPaths(ObsoleteIncPaths, ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename))); end; end; // removed directories still used fomr ObsoleteUnitPaths, ObsoleteIncPaths AnUnitInfo:=AProject.FirstPartOfProject; while AnUnitInfo<>nil do begin if FilenameIsAbsolute(AnUnitInfo.Filename) then begin if FilenameIsPascalUnit(AnUnitInfo.Filename) then ObsoleteUnitPaths:=RemoveSearchPaths(ObsoleteUnitPaths, ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename))); if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then ObsoleteIncPaths:=RemoveSearchPaths(ObsoleteIncPaths, ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename))); end; AnUnitInfo:=AnUnitInfo.NextPartOfProject; end; // check if compiler options contain paths of ObsoleteUnitPaths if ObsoleteUnitPaths<>'' then begin ProjUnitPaths:=AProject.CompilerOptions.OtherUnitFiles; p:=1; repeat OldP:=p; CurDir:=GetNextDirectoryInSearchPath(ProjUnitPaths,p); if CurDir='' then break; ResolvedDir:=AProject.CompilerOptions.ParsedOpts.DoParseOption(CurDir, pcosUnitPath,false); if (ResolvedDir<>'') and (SearchDirectoryInSearchPath(ObsoleteUnitPaths,ResolvedDir)>0) then begin if IDEQuestionDialog(lisRemoveUnitPath, Format(lisTheDirectoryContainsNoProjectUnitsAnyMoreRemoveThi, [CurDir]), mtConfirmation, [mrYes, lisRemove, mrNo, lisKeep2], '')=mrYes then begin // remove ProjUnitPaths:=RemoveSearchPaths(ProjUnitPaths,CurDir); p:=OldP; end; end; until false; AProject.CompilerOptions.OtherUnitFiles:=ProjUnitPaths; end; // check if compiler options contain paths of ObsoleteIncPaths if ObsoleteIncPaths<>'' then begin ProjIncPaths:=AProject.CompilerOptions.IncludePath; p:=1; repeat OldP:=p; CurDir:=GetNextDirectoryInSearchPath(ProjIncPaths,p); if CurDir='' then break; ResolvedDir:=AProject.CompilerOptions.ParsedOpts.DoParseOption(CurDir, pcosIncludePath,false); if (ResolvedDir<>'') and (SearchDirectoryInSearchPath(ObsoleteIncPaths,ResolvedDir)>0) then begin if IDEQuestionDialog(lisRemoveIncludePath, Format(lisTheDirectoryContainsNoProjectIncludeFilesAnyMoreRe, [CurDir]), mtConfirmation, [mrYes, lisRemove, mrNo, lisKeep2], '')=mrYes then begin // remove ProjIncPaths:=RemoveSearchPaths(ProjIncPaths,CurDir); p:=OldP; end; end; until false; AProject.CompilerOptions.IncludePath:=ProjIncPaths; end; finally // all changes were handled automatically by events, just clear the logs CodeToolBoss.SourceCache.ClearAllSourceLogEntries; AProject.EndUpdate; end; end; procedure TLazSourceFileManager.InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook); // close all source editors except the clicked var i, NeedSave, Idx: Integer; Ed: TSourceEditor; r: TModalResult; begin NeedSave := 0; for i := 0 to SrcNoteBook.EditorCount - 1 do begin //no need for CheckEditorNeedsSave ActiveSourcenoteBook (i = PageIndex) if (i <> PageIndex) and CheckEditorNeedsSave(SrcNoteBook.Editors[i], True) then begin inc(NeedSave); if NeedSave = 1 then Idx := i; end; end; if NeedSave = 1 then begin Ed := SrcNoteBook.Editors[Idx]; r := IDEQuestionDialog(lisSourceModified, Format(lisSourceOfPageHasChangedSave, ['"', Ed.PageName, '"']), mtConfirmation, [mrYes, lisMenuSave, mrNo, lisDiscardChanges, mrAbort]); case r of mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]); mrNo: ; // don't save mrAbort: exit; end; end else if NeedSave > 1 then begin for i := 0 to SrcNoteBook.EditorCount - 1 do begin if CheckEditorNeedsSave(SrcNoteBook.Editors[i], True) then begin dec(NeedSave); Ed := SrcNoteBook.Editors[i]; r := IDEQuestionDialog(lisSourceModified, Format(lisSourceOfPageHasChangedSaveExtended, ['"', Ed.PageName, '"', NeedSave]), mtConfirmation, [mrYes, lisMenuSave, mrAll, lisSaveAll, mrNo, lisDiscardChanges, mrIgnore, lisDiscardChangesAll, mrAbort]); case r of mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]); mrNo: ; // don't save mrAll: begin MainIDE.DoSaveAll([]); break end; mrIgnore: break; // don't save anymore mrAbort: exit; end; end; end; end; SourceEditorManager.IncUpdateLock; try repeat i:=SrcNoteBook.PageCount-1; if i=PageIndex then dec(i); if i<0 then break; if CloseEditorFile(SrcNoteBook.FindSourceEditorWithPageIndex(i),[])<>mrOk then exit; if imrOk then exit; Flags:=Flags-[sfSaveAs,sfSaveMainSourceAs]; end; // update HasResources information UpdateProjectResourceInfo; // save project info file if (not (sfSaveToTestDir in Flags)) and (not Project1.IsVirtual) then begin Result:=Project1.WriteProject([],''); if Result=mrAbort then exit; EnvironmentOptions.LastSavedProjectFile:=Project1.ProjectInfoFile; IDEProtocolOpts.LastProjectLoadingCrashed := False; AddRecentProjectFileToEnvironment(Project1.ProjectInfoFile); MainIDE.SaveIncludeLinks; MainIDE.UpdateCaption; if Result=mrAbort then exit; end; // save main source if (MainUnitInfo<>nil) and (not (sfDoNotSaveVirtualFiles in flags)) then begin if not (sfSaveToTestDir in Flags) then DestFilename := MainUnitInfo.Filename else DestFilename := MainBuildBoss.GetTestUnitFilename(MainUnitInfo); if MainUnitInfo.OpenEditorInfoCount > 0 then begin // loaded in source editor Result:=SaveEditorFile(MainUnitInfo.OpenEditorInfo[0].EditorComponent, [sfProjectSaving]+[sfSaveToTestDir,sfCheckAmbiguousFiles]*Flags); if Result=mrAbort then exit; end else begin // not loaded in source editor (hidden) SkipSavingMainSource := false; if not (sfSaveToTestDir in Flags) and not MainUnitInfo.NeedsSaveToDisk then SkipSavingMainSource := true; if (not SkipSavingMainSource) and (MainUnitInfo.Source<>nil) then begin Result:=SaveCodeBufferToFile(MainUnitInfo.Source, DestFilename); if Result=mrAbort then exit; end; end; // clear modified flags if not (sfSaveToTestDir in Flags) then begin if (Result=mrOk) then begin if MainUnitInfo<>nil then MainUnitInfo.ClearModifieds; if MainUnitSrcEdit<>nil then MainUnitSrcEdit.Modified:=false; end; end; end; Result:=mrOk; end; procedure TLazSourceFileManager.GetMainUnit(var MainUnitInfo: TUnitInfo; var MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean); begin MainUnitSrcEdit:=nil; if Project1.MainUnitID>=0 then begin MainUnitInfo:=Project1.MainUnitInfo; if MainUnitInfo.OpenEditorInfoCount > 0 then begin MainUnitSrcEdit := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent); if UpdateModified and MainUnitSrcEdit.Modified then begin MainUnitSrcEdit.UpdateCodeBuffer; end; end; end else MainUnitInfo:=nil; end; procedure TLazSourceFileManager.SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo); var ASrcEdit: TSourceEditor; begin ASrcEdit := TSourceEditor(AnEditorInfo.EditorComponent); if ASrcEdit=nil then exit; AnEditorInfo.TopLine:=ASrcEdit.EditorComponent.TopLine; AnEditorInfo.CursorPos:=ASrcEdit.EditorComponent.CaretXY; AnEditorInfo.FoldState := ASrcEdit.EditorComponent.FoldState; end; procedure TLazSourceFileManager.SaveSourceEditorProjectSpecificSettings; var i: Integer; begin for i := 0 to Project1.AllEditorsInfoCount - 1 do SaveSrcEditorProjectSpecificSettings(Project1.AllEditorsInfo[i]); end; procedure TLazSourceFileManager.UpdateProjectResourceInfo; var AnUnitInfo: TUnitInfo; LFMFilename: String; begin AnUnitInfo:=Project1.FirstPartOfProject; while AnUnitInfo<>nil do begin if (not AnUnitInfo.HasResources) and (not AnUnitInfo.IsVirtual) and FilenameIsPascalUnit(AnUnitInfo.Filename) then begin LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm'); if not FileExistsUTF8(LFMFilename) then LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.dfm'); AnUnitInfo.HasResources:=FileExistsUTF8(LFMFilename); end; AnUnitInfo:=AnUnitInfo.NextPartOfProject; end; end; function TLazSourceFileManager.ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult; var MainUnitSrcEdit: TSourceEditor; MainUnitInfo: TUnitInfo; SaveDialog: TSaveDialog; NewLPIFilename, NewProgramFilename, NewProgramName, AText, ACaption, Ext: string; NewBuf: TCodeBuffer; OldProjectDir: string; TitleWasDefault: Boolean; OldSource: String; AFilename: String; NewTargetFilename: String; begin Project1.BeginUpdate(false); try OldProjectDir:=Project1.ProjectDirectory; if Project1.MainUnitInfo = nil then UseMainSourceFile := False; SaveDialog:=TSaveDialog.Create(nil); try InputHistories.ApplyFileDialogSettings(SaveDialog); AFilename:=''; // build a nice project info filename suggestion if UseMainSourceFile and (Project1.MainUnitID>=0) then AFilename:=Project1.MainUnitInfo.Unit_Name; if AFilename='' then AFilename:=ExtractFileName(Project1.ProjectInfoFile); if AFilename='' then AFilename:=ExtractFileName(Project1.MainFilename); if AFilename='' then AFilename:=Trim(Project1.GetTitle); if AFilename='' then AFilename:='project1'; Ext := LowerCase(ExtractFileExt(AFilename)); if UseMainSourceFile then begin if (Ext = '') or (not FilenameIsPascalSource(AFilename)) then AFilename := ChangeFileExt(AFilename, '.pas'); end else begin if (Ext = '') or FilenameIsPascalSource(AFilename) then AFilename := ChangeFileExt(AFilename, '.lpi'); end; Ext := ExtractFileExt(AFilename); SaveDialog.Title := Format(lisSaveProject, [Project1.GetTitleOrName, Ext]); SaveDialog.FileName := AFilename; SaveDialog.Filter := '*' + Ext + '|' + '*' + Ext; SaveDialog.DefaultExt := ExtractFileExt(AFilename); if not Project1.IsVirtual then SaveDialog.InitialDir := Project1.ProjectDirectory; repeat Result:=mrCancel; NewLPIFilename:=''; // the project info file name NewProgramName:=''; // the pascal program identifier NewProgramFilename:=''; // the program source filename if not SaveDialog.Execute then begin // user cancels Result:=mrCancel; exit; end; AFilename:=ExpandFileNameUTF8(SaveDialog.FileName); if not FilenameIsAbsolute(AFilename) then RaiseException('TLazSourceFileManager.ShowSaveProjectAsDialog: buggy ExpandFileNameUTF8'); // check program name NewProgramName:=ExtractFileNameOnly(AFilename); if (NewProgramName='') or (not IsValidUnitName(NewProgramName)) then begin Result:=IDEMessageDialog(lisInvalidProjectFilename, Format(lisisAnInvalidProjectNamePleaseChooseAnotherEGProject, ['"', SaveDialog.Filename, '"', LineEnding]), mtInformation,[mbRetry,mbAbort]); if Result=mrAbort then exit; continue; // try again end; // append default extension if UseMainSourceFile then begin NewLPIFilename:=ChangeFileExt(AFilename,'.lpi'); end else begin NewLPIFilename:=AFilename; if ExtractFileExt(NewLPIFilename)='' then NewLPIFilename:=NewLPIFilename+'.lpi'; end; // apply naming conventions // rename to lowercase is not needed for main source if Project1.MainUnitID >= 0 then begin // check mainunit filename Ext := ExtractFileExt(Project1.MainUnitInfo.Filename); if Ext = '' then Ext := '.pas'; if UseMainSourceFile then NewProgramFilename := ExtractFileName(AFilename) else NewProgramFilename := ExtractFileNameWithoutExt(NewProgramName) + Ext; NewProgramFilename := ExtractFilePath(NewLPIFilename) + NewProgramFilename; if (CompareFilenames(NewLPIFilename, NewProgramFilename) = 0) then begin ACaption:=lisChooseADifferentName; AText:=Format(lisTheProjectInfoFileIsEqualToTheProjectMainSource, [ '"', NewLPIFilename, '"', LineEnding]); Result:=IDEMessageDialog(ACaption, AText, mtError, [mbAbort,mbRetry]); if Result=mrAbort then exit; continue; // try again end; // check programname if FilenameIsPascalUnit(NewProgramFilename) and (Project1.IndexOfUnitWithName(NewProgramName,true, Project1.MainUnitInfo)>=0) then begin ACaption:=lisUnitIdentifierExists; AText:=Format(lisThereIsAUnitWithTheNameInTheProjectPleaseChoose, ['"', NewProgramName, '"', LineEnding]); Result:=IDEMessageDialog(ACaption,AText,mtError,[mbRetry,mbAbort]); if Result=mrAbort then exit; continue; // try again end; Result:=mrOk; end else begin NewProgramFilename:=''; Result:=mrOk; end; until Result<>mrRetry; finally InputHistories.StoreFileDialogSettings(SaveDialog); SaveDialog.Free; end; //DebugLn(['TLazSourceFileManager.ShowSaveProjectAsDialog NewLPI=',NewLPIFilename,' NewProgramName=',NewProgramName,' NewMainSource=',NewProgramFilename]); // check if info file or source file already exists if FileExistsUTF8(NewLPIFilename) then begin ACaption:=lisOverwriteFile; AText:=Format(lisAFileAlreadyExistsReplaceIt, ['"', NewLPIFilename, '"', LineEnding]); Result:=IDEMessageDialog(ACaption, AText, mtConfirmation, [mbOk, mbCancel]); if Result=mrCancel then exit; end else begin if FileExistsUTF8(NewProgramFilename) then begin ACaption:=lisOverwriteFile; AText:=Format(lisAFileAlreadyExistsReplaceIt, ['"', NewProgramFilename, '"', LineEnding]); Result:=IDEMessageDialog(ACaption, AText, mtConfirmation,[mbOk,mbCancel]); if Result=mrCancel then exit; end; end; TitleWasDefault := Project1.TitleIsDefault(true); // set new project target filename if (Project1.TargetFilename<>'') and ((SysUtils.CompareText(ExtractFileNameOnly(Project1.TargetFilename), ExtractFileNameOnly(Project1.ProjectInfoFile))=0) or (Project1.ProjectInfoFile='')) then begin // target file is default => change, but keep sub directories // Note: Extension is appended automatically => do not add it NewTargetFilename:=ExtractFilePath(Project1.TargetFilename) +ExtractFileNameOnly(NewProgramFilename); Project1.TargetFilename:=NewTargetFilename; //DebugLn(['TLazSourceFileManager.ShowSaveProjectAsDialog changed targetfilename to ',Project1.TargetFilename]); end; // set new project filename Project1.ProjectInfoFile:=NewLPIFilename; EnvironmentOptions.AddToRecentProjectFiles(NewLPIFilename); MainIDE.SetRecentProjectFilesMenu; // change main source if (Project1.MainUnitID >= 0) then begin GetMainUnit(MainUnitInfo, MainUnitSrcEdit, true); if not Project1.ProjResources.RenameDirectives(MainUnitInfo.Filename,NewProgramFilename) then begin DebugLn(['TLazSourceFileManager.ShowSaveProjectAsDialog failed renaming directives Old="',MainUnitInfo.Filename,'" New="',NewProgramFilename,'"']); // silently ignore end; // Save old source code, to prevent overwriting it, // if the file name didn't actually change. OldSource := MainUnitInfo.Source.Source; // switch MainUnitInfo.Source to new code NewBuf := CodeToolBoss.CreateFile(NewProgramFilename); if NewBuf=nil then begin Result:=IDEMessageDialog(lisErrorCreatingFile, Format(lisUnableToCreateFile3, [ LineEnding, '"', NewProgramFilename, '"']), mtError, [mbCancel]); exit; end; // copy the source to the new buffer NewBuf.Source:=OldSource; // assign the new buffer to the MainUnit MainUnitInfo.Source:=NewBuf; if MainUnitSrcEdit<>nil then MainUnitSrcEdit.CodeBuffer:=NewBuf; // change program name MainUnitInfo.Unit_Name:=NewProgramName; MainUnitInfo.Modified:=true; // update source notebook page names UpdateSourceNames; end; // update paths Project1.CompilerOptions.OtherUnitFiles:= RebaseSearchPath(Project1.CompilerOptions.OtherUnitFiles,OldProjectDir, Project1.ProjectDirectory,true); Project1.CompilerOptions.IncludePath:= RebaseSearchPath(Project1.CompilerOptions.IncludePath,OldProjectDir, Project1.ProjectDirectory,true); Project1.CompilerOptions.Libraries:= RebaseSearchPath(Project1.CompilerOptions.Libraries,OldProjectDir, Project1.ProjectDirectory,true); Project1.CompilerOptions.ObjectPath:= RebaseSearchPath(Project1.CompilerOptions.ObjectPath,OldProjectDir, Project1.ProjectDirectory,true); Project1.CompilerOptions.SrcPath:= RebaseSearchPath(Project1.CompilerOptions.SrcPath,OldProjectDir, Project1.ProjectDirectory,true); Project1.CompilerOptions.DebugPath:= RebaseSearchPath(Project1.CompilerOptions.DebugPath,OldProjectDir, Project1.ProjectDirectory,true); // change title if TitleWasDefault then begin Project1.Title:=Project1.GetDefaultTitle; // title does not need to be removed from source, because it was default end; // invalidate cached substituted macros IncreaseCompilerParseStamp; finally Project1.EndUpdate; end; Result:=mrOk; //DebugLn(['TLazSourceFileManager.ShowSaveProjectAsDialog END OK']); end; function TLazSourceFileManager.AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult; var DataModified: Boolean; SrcModified: Boolean; begin if Project1=nil then exit(mrOk); if not SomethingOfProjectIsModified then exit(mrOk); DataModified:=Project1.SomeDataModified(false); SrcModified:=SourceEditorManager.SomethingModified(false); if Project1.IsVirtual and (not DataModified) and (not SrcModified) then begin // only session changed of a new project => ignore exit(mrOk) end; if (Project1.SessionStorage=pssInProjectInfo) or DataModified then begin // lpi file will change => ask Result:=IDEQuestionDialog(lisProjectChanged, Format(lisSaveChangesToProject, [Project1.GetTitleOrName]), mtConfirmation, [mrYes, mrNoToAll, lisNo, mbCancel], ''); if Result=mrNoToAll then exit(mrOk); if Result<>mrYes then exit(mrCancel); end else if SrcModified then begin // some non project files were changes in the source editor Result:=IDEQuestionDialog(lisSaveChangedFiles,lisSaveChangedFiles, mtConfirmation, [mrYes, mrNoToAll, lisNo, mbCancel], ''); if Result=mrNoToAll then exit(mrOk); if Result<>mrYes then exit(mrCancel); end else begin // only session data changed if Project1.SessionStorage=pssNone then // session is not saved => skip exit(mrOk) else if not SomethingOfProjectIsModified then // no change exit(mrOk) else begin // session is saved separately if EnvironmentOptions.AskSaveSessionOnly then begin Result:=IDEQuestionDialog(lisProjectSessionChanged, Format(lisSaveSessionChangesToProject, [Project1.GetTitleOrName]), mtConfirmation, [mrYes, mrNoToAll, lisNo, mbCancel], ''); if Result=mrNoToAll then exit(mrOk); if Result<>mrYes then exit(mrCancel); end; end; end; Result:=SaveProject([sfCanAbort]); if Result=mrAbort then exit; if Result<>mrOk then begin Result:=IDEQuestionDialog(lisChangesWereNotSaved, ContinueText, mtConfirmation, [mrOk, ContinueBtn, mrAbort]); if Result<>mrOk then exit(mrCancel); end; end; function TLazSourceFileManager.SaveSourceEditorChangesToCodeCache( AEditor: TSourceEditorInterface): boolean; // save all open sources to code tools cache procedure SaveChanges(SaveEditor: TSourceEditorInterface); var AnUnitInfo: TUnitInfo; begin AnUnitInfo := Project1.UnitWithEditorComponent(SaveEditor); if (AnUnitInfo<>nil) then begin //debugln(['SaveChanges ',AnUnitInfo.Filename,' ',SaveEditor.NeedsUpdateCodeBuffer]); if SaveEditor.NeedsUpdateCodeBuffer then begin SaveSourceEditorChangesToCodeCache:=true; SaveEditor.UpdateCodeBuffer; //debugln(['TLazSourceFileManager.SaveSourceEditorChangesToCodeCache.SaveChanges ',AnUnitInfo.Filename,' Step=',TCodeBuffer(SaveEditor.CodeToolsBuffer).ChangeStep]); end; end; end; var i: integer; begin Result:=false; //debugln(['TLazSourceFileManager.SaveSourceEditorChangesToCodeCache ']); if AEditor = nil then begin for i:=0 to SourceEditorManager.SourceEditorCount - 1 do SaveChanges(SourceEditorManager.SourceEditors[i]); end else begin SaveChanges(AEditor); end; end; function TLazSourceFileManager.ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string; IgnoreErrors, Quiet, Confirm: boolean): TModalResult; // Replaces all references to a unit var OwnerList: TFPList; ExtraFiles: TStrings; Files: TStringList; OldCode: TCodeBuffer; OldCodeCreated: Boolean; PascalReferences: TAVLTree; i: Integer; MsgResult: TModalResult; OnlyEditorFiles: Boolean; aFilename: String; begin if (CompareFilenames(OldFilename,NewFilename)=0) and (OldUnitName=NewUnitName) then // compare unitnames case sensitive, maybe only the case changed exit(mrOk); OnlyEditorFiles:=not FilenameIsAbsolute(OldFilename); // this was a new file, files on disk can not refer to it OwnerList:=nil; OldCode:=nil; OldCodeCreated:=false; PascalReferences:=nil; Files:=TStringList.Create; try if OnlyEditorFiles then begin // search only in open files for i:=0 to SourceEditorManagerIntf.UniqueSourceEditorCount-1 do begin aFilename:=SourceEditorManagerIntf.UniqueSourceEditors[i].FileName; if not FilenameIsPascalSource(aFilename) then continue; Files.Add(aFileName); end; // add project's main source file if (Project1<>nil) and (Project1.MainUnitID>=0) then Files.Add(Project1.MainFilename); end else begin // get owners of unit OwnerList:=PkgBoss.GetOwnersOfUnit(NewFilename); if OwnerList=nil then exit(mrOk); PkgBoss.ExtendOwnerListWithUsedByOwners(OwnerList); ReverseList(OwnerList); // get source files of packages and projects ExtraFiles:=PkgBoss.GetSourceFilesOfOwners(OwnerList); try if ExtraFiles<>nil then Files.AddStrings(ExtraFiles); finally ExtraFiles.Free; end; end; for i:=Files.Count-1 downto 0 do begin if (CompareFilenames(Files[i],OldFilename)=0) or (CompareFilenames(Files[i],NewFilename)=0) then Files.Delete(i); end; //DebugLn(['TLazSourceFileManager.ReplaceUnitUse ',Files.Text]); // commit source editor to codetools SaveSourceEditorChangesToCodeCache(nil); // load or create old unit OldCode:=CodeToolBoss.LoadFile(OldFilename,true,false); if OldCode=nil then begin // create old file in memory so that unit search can find it OldCode:=CodeToolBoss.CreateFile(OldFilename); OldCodeCreated:=true; end; // search pascal source references Result:=GatherUnitReferences(Files,OldCode,false,IgnoreErrors,true, PascalReferences); if (not IgnoreErrors) and (not Quiet) and (CodeToolBoss.ErrorMessage<>'') then MainIDE.DoJumpToCodeToolBossError; if Result<>mrOk then begin debugln('TLazSourceFileManager.ReplaceUnitUse GatherUnitReferences failed'); exit; end; // replace if (PascalReferences<>nil) and (PascalReferences.Count>0) then begin if Confirm then begin MsgResult:=IDEQuestionDialog(lisUpdateReferences, Format(lisTheUnitIsUsedByOtherFilesUpdateReferencesAutomatic, [OldUnitName, LineEnding]), mtConfirmation, [mrYes,mrNo,mrYesToAll,mrNoToAll],''); case MsgResult of mrYes: ; mrYesToAll: EnvironmentOptions.UnitRenameReferencesAction:=urraAlways; mrNoToAll: begin EnvironmentOptions.UnitRenameReferencesAction:=urraNever; Result:=mrOk; exit; end; else Result:=mrOk; exit; end; end; if not CodeToolBoss.RenameIdentifier(PascalReferences, OldUnitName,NewUnitName) then begin if (not IgnoreErrors) and (not Quiet) then MainIDE.DoJumpToCodeToolBossError; debugln('TLazSourceFileManager.ReplaceUnitUse unable to commit'); if not IgnoreErrors then begin Result:=mrCancel; exit; end; end; end; finally if OldCodeCreated then OldCode.IsDeleted:=true; CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences); OwnerList.Free; Files.Free; end; //PkgBoss.GetOwnersOfUnit(NewFilename); Result:=mrOk; end; function TLazSourceFileManager.DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean; var ActiveSourceEditor: TSourceEditor; ActiveUnitInfo: TUnitInfo; begin Assert(Assigned(aLookupRoot),'SourceFileMgr.DesignerUnitIsVirtual: aLookupRoot is not assigned'); MainIDE.GetUnitWithPersistent(aLookupRoot, ActiveSourceEditor, ActiveUnitInfo); Result := ActiveUnitInfo.IsVirtual; end; finalization SourceFileMgrSingleton.Free; end.