lazarus/ide/project.pp

7809 lines
257 KiB
ObjectPascal

{
/***************************************************************************
project.pp - project utility class file
-----------------------------------------
TProject is responsible for managing a complete project.
Initial Revision : Sun Mar 28 23:15:32 CST 1999
***************************************************************************/
***************************************************************************
* *
* 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 <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
}
unit Project;
{$mode objfpc}{$H+}
{$ifdef Trace}
{$ASSERTIONS ON}
{$endif}
interface
{$I ide.inc}
uses
{$IFDEF IDE_MEM_CHECK}
MemCheck,
{$ENDIF}
// RTL + FCL
Classes, SysUtils, TypInfo, System.UITypes,
// LCL
LCLProc, Forms, Dialogs,
// CodeTools
CodeToolsConfig, ExprEval, DefineTemplates, BasicCodeTools, CodeToolsCfgScript,
LinkScanner, CodeToolManager, CodeCache, CodeTree, StdCodeTools,
// LazUtils
FPCAdds, LazUtilities, FileUtil, LazFileUtils, LazFileCache, LazMethodList,
LazLoggerBase, FileReferenceList, LazUTF8, Laz2_XMLCfg, Maps, AvgLvlTree,
// BuildIntf
BaseIDEIntf, ProjectIntf, PackageIntf, MacroIntf, MacroDefIntf,
CompOptsIntf, IDEOptionsIntf,
// IDEIntf
PropEdits, UnitResources, EditorSyntaxHighlighterDef, InputHistory, SrcEditorIntf,
IDEOptEditorIntf, IDEDialogs,
// IdeConfig
EnvironmentOpts, LazConf, TransferMacros, SearchPathProcs, IdeXmlConfigProcs,
IDECmdLine, IDEProcs, CompOptsModes, ModeMatrixOpts,
ParsedCompilerOpts, CompilerOptions,
// IDE
ProjectResources, ProjectIcon, RunParamsOpts,
ProjectDefs, EditDefineTree, LazarusIDEStrConsts,
ProjPackCommon, PackageDefs, PackageSystem;
type
TUnitInfo = class;
TProject = class;
TOnFileBackup = function(const FileToBackup: string):TModalResult of object;
TOnUnitNameChange = procedure(AnUnitInfo: TUnitInfo;
const OldUnitName, NewUnitName: string;
CheckIfAllowed: boolean;
var Allowed: boolean) of object;
TOnLoadProjectInfo = procedure(TheProject: TProject; XMLConfig: TXMLConfig;
Merge: boolean) of object;
TOnSaveProjectInfo = procedure(TheProject: TProject;
XMLConfig: TXMLConfig; WriteFlags: TProjectWriteFlags) of object;
TOnChangeProjectInfoFile = procedure(TheProject: TProject) of object;
TOnSaveUnitSessionInfoInfo = procedure(AUnitInfo: TUnitInfo) of object;
TUnitInfoList = (
uilPartOfProject,
uilWithEditorIndex,
uilWithComponent,
uilLoaded,
uilAutoRevertLocked
);
TUnitCompDependencyList = (
ucdlRequires,
ucdlUsedBy
);
TUnitCompDependencyType = (
ucdtAncestor, // RequiresUnit is ancestor
ucdtProperty, // a property references RequiresUnit's component or sub component
ucdtOldProperty, // like ucdtProperty, but for the old state before the revert
ucdtInlineClass // RequiresUnit is class of an inline component
);
TUnitCompDependencyTypes = set of TUnitCompDependencyType;
const
AllUnitCompDependencyTypes = [low(TUnitCompDependencyType)..high(TUnitCompDependencyType)];
// Names for extra buildmodes which may be created automatically.
DebugModeName = 'Debug';
ReleaseModeName = 'Release';
type
{ TUCDComponentProperty }
TUCDComponentProperty = class
public
UsedByPropPath: string;
RequiresPropPath: string;
constructor Create(const SrcPath, DestPath: string);
end;
{ TUnitComponentDependency }
TUnitComponentDependency = class
private
FCompProps: TFPList;// list of TUCDComponentProperty
FRequiresUnit: TUnitInfo;
FTypes: TUnitCompDependencyTypes;
FUsedByUnit: TUnitInfo;
function GetCompPropCount: integer;
function GetCompProps(Index: integer): TUCDComponentProperty;
procedure SetRequiresUnit(const AValue: TUnitInfo);
procedure SetTypes(const AValue: TUnitCompDependencyTypes);
procedure SetUsedByUnit(const AValue: TUnitInfo);
public
NextDependency,PrevDependency: array[TUnitCompDependencyList] of TUnitComponentDependency;
constructor Create;
destructor Destroy; override;
procedure ClearComponentProperties;
function NextUsedByDependency: TUnitComponentDependency;
function PrevUsedByDependency: TUnitComponentDependency;
function NextRequiresDependency: TUnitComponentDependency;
function PrevRequiresDependency: TUnitComponentDependency;
procedure AddToList(var FirstDependency: TUnitComponentDependency;
ListType: TUnitCompDependencyList);
procedure RemoveFromList(var FirstDependency: TUnitComponentDependency;
ListType: TUnitCompDependencyList);
property RequiresUnit: TUnitInfo read FRequiresUnit write SetRequiresUnit;
property UsedByUnit: TUnitInfo read FUsedByUnit write SetUsedByUnit;
property Types: TUnitCompDependencyTypes read FTypes write SetTypes;
property CompPropCount: integer read GetCompPropCount;
property CompProps[Index: integer]: TUCDComponentProperty read GetCompProps;
function FindUsedByPropPath(const UsedByPropPath: string): TUCDComponentProperty;
function SetUsedByPropPath(const UsedByPropPath, RequiresPropPath: string
): TUCDComponentProperty;
function CreatePropPath(AComponent: TComponent;
const PropName: string = ''): string;
end;
//---------------------------------------------------------------------------
TUnitInfoFlag = (
uifAutoReferenceSourceDir,
uifBuildFileIfActive,
uifComponentUsedByDesigner,
uifComponentIndirectlyUsedByDesigner,
uifCustomDefaultHighlighter,
uifDisableI18NForLFM,
uifFileReadOnly,
uifHasErrorInLFM,
uifHasResources, // source has resource file
uifInternalFile, // data from an internal source (e.g. an editor macro (pascal script) from memory)
uifLoaded, // loaded in the source editor, needed to restore open files
uifLoadedDesigner, // has a visible designer, needed to restore open designers
uifLoadingComponent,
uifMarked,
uifModified,
uifRunFileIfActive,
uifSessionModified,
uifUserReadOnly
);
TUnitInfoFlags = set of TUnitInfoFlag;
{ TUnitEditorInfo }
TUnitEditorInfo = class
private
FEditorComponent: TSourceEditorInterface;
FUnitInfo: TUnitInfo;
procedure SetEditorComponent(const AValue: TSourceEditorInterface);
private
FIsLocked: Boolean;
FIsVisibleTab: Boolean;
FPageIndex: integer;
FWindowID: integer;
FTopLine: integer;
FCursorPos: TPoint; // physical (screen) position
FFoldState: String;
// Todo: FCustomHighlighter is only ever set to false, and not stored in XML
FCustomHighlighter: boolean; // do not change highlighter on file extension change
FSyntaxHighlighter: TIdeSyntaxHighlighterID;
procedure SetCursorPos(const AValue: TPoint);
procedure SetFoldState(AValue: String);
procedure SetIsLocked(const AValue: Boolean);
procedure SetPageIndex(const AValue: Integer);
procedure SetIsVisibleTab(const AValue: Boolean);
procedure SetSyntaxHighlighter(AValue: TIdeSyntaxHighlighterID);
procedure SetTopLine(const AValue: Integer);
procedure SetWindowIndex(const AValue: Integer);
protected
procedure Clear;
public
constructor Create(aUnitInfo: TUnitInfo);
destructor Destroy; override;
property UnitInfo: TUnitInfo read FUnitInfo;
property EditorComponent: TSourceEditorInterface
read FEditorComponent write SetEditorComponent;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string; SaveFold: Boolean);
public
property IsVisibleTab: Boolean read FIsVisibleTab write SetIsVisibleTab;
property PageIndex: Integer read FPageIndex write SetPageIndex;
property WindowID: Integer read FWindowID write SetWindowIndex;
property TopLine: Integer read FTopLine write SetTopLine;
property CursorPos: TPoint read FCursorPos write SetCursorPos;
property FoldState: String read FFoldState write SetFoldState;
property IsLocked: Boolean read FIsLocked write SetIsLocked;
property CustomHighlighter: Boolean read FCustomHighlighter write FCustomHighlighter; // SetCustomHighlighter
property SyntaxHighlighter: TIdeSyntaxHighlighterID read FSyntaxHighlighter write SetSyntaxHighlighter; // SetSyntaxHighlighter
end;
{ TUnitEditorInfoList }
TUnitEditorInfoList = class
private
FList: TFPList;
FUnitInfo: TUnitInfo;
function GetClosedEditorInfos(Index: Integer): TUnitEditorInfo;
function GetEditorInfos(Index: Integer): TUnitEditorInfo;
function GetOpenEditorInfos(Index: Integer): TUnitEditorInfo;
protected
procedure ClearEachInfo;
procedure SortByPageIndex;
procedure SetLastUsedEditor(AEditor:TSourceEditorInterface);
procedure MakeUsedEditorInfo(AEditorInfo: TUnitEditorInfo);
procedure MakeUnUsedEditorInfo(AEditorInfo: TUnitEditorInfo);
procedure Clear;
public
constructor Create(aUnitInfo: TUnitInfo);
destructor Destroy; override;
property EditorInfos[Index: Integer]: TUnitEditorInfo read GetEditorInfos; default;
property OpenEditorInfos[Index: Integer]: TUnitEditorInfo read GetOpenEditorInfos;
property ClosedEditorInfos[Index: Integer]: TUnitEditorInfo read GetClosedEditorInfos;
function Count: Integer;
function OpenCount: Integer;
function ClosedCount: Integer;
function IndexOfEditorComponent(anEditor: TSourceEditorInterface): Integer;
function NewEditorInfo: TUnitEditorInfo;
procedure Add(AEditorInfo: TUnitEditorInfo);
procedure Delete(Index: Integer);
procedure Remove(AEditorInfo: TUnitEditorInfo);
end;
{ TUnitInfo }
TUnitInfo = class(TLazProjectFile)
private
FComponentTypesToClasses: TStringToPointerTree;
FComponentVarsToClasses: TStringToPointerTree;
FDefaultSyntaxHighlighter: TIdeSyntaxHighlighterID;
FEditorInfoList: TUnitEditorInfoList;
fAutoRevertLockCount: integer;// =0 means, codetools can auto update from disk
fBookmarks: TFileBookmarks;
fComponent: TComponent;
FComponentState: TWindowState; // state of component when we save it
FResourceBaseClass: TPFComponentBaseClass;
fComponentName: string; { classname is always T<ComponentName>
this attribute contains the component name,
even if the unit is not loaded, or the designer form is not created.
A component can be for example a TForm or a TDataModule }
fComponentResourceName: string;
FComponentLastBinStreamSize: TStreamSeekType;
FComponentLastLFMStreamSize: TStreamSeekType;
FComponentLastLRSStreamSize: TStreamSeekType;
FDirectives: TStrings;
fFileName: string; // with path = saved, without path = not yet saved
FFirstRequiredComponent: TUnitComponentDependency;
FFirstUsedByComponent: TUnitComponentDependency;
FFlags: TUnitInfoFlags;
fNext, fPrev: array[TUnitInfoList] of TUnitInfo;
fOnFileBackup: TOnFileBackup;
fOnLoadSaveFilename: TOnLoadSaveFilename;
FOnUnitNameChange: TOnUnitNameChange;
FProject: TProject;
FResourceBaseClassname: string;
FRevertLockCount: integer;// >0 means IDE is currently reverting this unit
fSource: TCodeBuffer;
FSourceLFM: TCodeBuffer;
fUsageCount: extended;
fSourceChangeStep: LongInt;
FSourceDirectoryReferenced: boolean;
fLastDirectoryReferenced: string;
FSetBookmarLock: Integer;
FUnitResourceFileformat: TUnitResourcefileFormatClass;
function ComponentLFMOnDiskHasChanged: boolean;
function GetAutoReferenceSourceDir: boolean;
function GetBuildFileIfActive: boolean;
function GetCustomDefaultHighlighter: boolean;
function GetDisableI18NForLFM: boolean;
function GetEditorInfo(Index: Integer): TUnitEditorInfo;
function GetFileReadOnly: Boolean;
function GetHasErrorInLFM: boolean;
function GetHasResources: boolean;
function GetInternalFile: boolean;
function GetLoaded: Boolean;
function GetLoadedDesigner: Boolean;
function GetLoadingComponent: boolean;
function GetModified: boolean;
function GetNextAutoRevertLockedUnit: TUnitInfo;
function GetNextLoadedUnit: TUnitInfo;
function GetNextPartOfProject: TUnitInfo;
function GetNextUnitWithComponent: TUnitInfo;
function GetNextUnitWithEditorIndex: TUnitInfo;
function GetOpenEditorInfo(Index: Integer): TUnitEditorInfo;
function GetPrevAutoRevertLockedUnit: TUnitInfo;
function GetPrevLoadedUnit: TUnitInfo;
function GetPrevPartOfProject: TUnitInfo;
function GetPrevUnitWithComponent: TUnitInfo;
function GetPrevUnitWithEditorIndex: TUnitInfo;
function GetRunFileIfActive: boolean;
function GetSessionModified: boolean;
function GetUnitResourceFileformat: TUnitResourcefileFormatClass;
function GetUserReadOnly: Boolean;
procedure SetAutoReferenceSourceDir(const AValue: boolean);
procedure SetBuildFileIfActive(const AValue: boolean);
procedure SetCustomDefaultHighlighter(AValue: boolean);
procedure SetDefaultSyntaxHighlighter(const AValue: TIdeSyntaxHighlighterID);
procedure SetDisableI18NForLFM(const AValue: boolean);
procedure SetFileReadOnly(const AValue: Boolean);
procedure SetComponent(const AValue: TComponent);
procedure SetHasErrorInLFM(AValue: boolean);
procedure SetHasResources(AValue: boolean);
procedure SetInternalFile(AValue: boolean);
procedure SetLoaded(const AValue: Boolean);
procedure SetLoadedDesigner(const AValue: Boolean);
procedure SetLoadingComponent(AValue: boolean);
procedure SetModified(const AValue: boolean);
procedure SetProject(const AValue: TProject);
procedure SetRunFileIfActive(const AValue: boolean);
procedure SetSessionModified(const AValue: boolean);
procedure SetSource(ABuffer: TCodeBuffer);
procedure SetSourceLFM(const AValue: TCodeBuffer);
procedure SetTimeStamps;
procedure SetUserReadOnly(const NewValue: boolean);
protected
function GetFileName: string; override;
procedure SetFilename(const AValue: string); override;
procedure SetIsPartOfProject(const AValue: boolean); override;
procedure UpdateList(ListType: TUnitInfoList; Add: boolean);
procedure SetInternalFilename(const NewFilename: string);
procedure SetUnitName(const AValue: string); override;
procedure UpdateHasCustomHighlighter(aDefaultHighlighter: TIdeSyntaxHighlighterID);
procedure UpdatePageIndex;
public
constructor Create(ACodeBuffer: TCodeBuffer);
destructor Destroy; override;
function GetFileOwner: TObject; override;
function GetFileOwnerName: string; override;
function IsChangedOnDisk(CheckLFM: boolean): boolean;
function IsAutoRevertLocked: boolean;
function IsReverting: boolean;
function IsMainUnit: boolean;
function IsVirtual: boolean;
function GetDirectory: string;
function GetFullFilename: string; override;
function GetShortFilename(UseUp: boolean): string; override;
function NeedsSaveToDisk(CheckLFM: boolean): boolean;
function ReadOnly: boolean;
function ReadUnitSource(ReadUnitName,Revert:boolean): TModalResult;
function ShortFilename: string;
function WriteUnitSource: TModalResult;
function WriteUnitSourceToFile(const AFileName: string): TModalResult;
procedure Clear;
procedure ClearModifieds; override;
procedure ClearComponentDependencies;
procedure WriteDebugReportUnitComponentDependencies(Prefix: string);
procedure IgnoreCurrentFileDateOnDisk;
procedure IncreaseAutoRevertLock; // do not auto revert from disk
procedure DecreaseAutoRevertLock;
function ReadUnitNameFromSource(TryCache: boolean): string;// fetch unit name from source and update property UnitName
function GetUsesUnitName: string;
function CreateUnitName: string;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
Merge, IgnoreIsPartOfProject: boolean;
FileVersion: integer);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveData, SaveSession: boolean;
UsePathDelim: TPathDelimSwitch);
procedure UpdateUsageCount(Min, IfBelowThis, IncIfBelow: extended);
procedure UpdateUsageCount(TheUsage: TUnitUsage; const Factor: TDateTime);
procedure UpdateSourceDirectoryReference;
procedure SetSourceText(const SourceText: string; Beautify: boolean = false); override;
function GetSourceText: string; override;
// component dependencies
function AddRequiresComponentDependency(RequiredUnit: TUnitInfo;
Types: TUnitCompDependencyTypes
): TUnitComponentDependency;
procedure RemoveRequiresComponentDependency(RequiredUnit: TUnitInfo;
Types: TUnitCompDependencyTypes);
function FindComponentDependency(RequiredUnit: TUnitInfo
): TUnitComponentDependency;
function FindRequiredComponentDependency(MinTypes: TUnitCompDependencyTypes
): TUnitComponentDependency;
function FindUsedByComponentDependency(MinTypes: TUnitCompDependencyTypes
): TUnitComponentDependency;
function FindAncestorUnit: TUnitInfo;
procedure ClearUnitComponentDependencies(ClearTypes: TUnitCompDependencyTypes);
// Bookmarks
function AddBookmark(X, Y, ID: integer):integer;
procedure DeleteBookmark(ID: integer);
// EditorInfo
// At any time, any UnitInfo has at least one EditorInfo
function EditorInfoCount: Integer;
property EditorInfo[Index: Integer]: TUnitEditorInfo read GetEditorInfo;
function OpenEditorInfoCount: Integer; // with EditorComponent assigned
property OpenEditorInfo[Index: Integer]: TUnitEditorInfo read GetOpenEditorInfo;
function GetClosedOrNewEditorInfo: TUnitEditorInfo;
procedure SetLastUsedEditor(AEditor:TSourceEditorInterface);
// Highlighter
procedure UpdateDefaultHighlighter(aDefaultHighlighter: TIdeSyntaxHighlighterID);
public
{ Properties }
property UnitResourceFileformat: TUnitResourcefileFormatClass read GetUnitResourceFileformat;
// Unit lists
property NextUnitWithEditorIndex: TUnitInfo read GetNextUnitWithEditorIndex;
property PrevUnitWithEditorIndex: TUnitInfo read GetPrevUnitWithEditorIndex;
property NextUnitWithComponent: TUnitInfo read GetNextUnitWithComponent;
property PrevUnitWithComponent: TUnitInfo read GetPrevUnitWithComponent;
property NextLoadedUnit: TUnitInfo read GetNextLoadedUnit;
property PrevLoadedUnit: TUnitInfo read GetPrevLoadedUnit;
property NextAutoRevertLockedUnit: TUnitInfo read GetNextAutoRevertLockedUnit;
property PrevAutoRevertLockedUnit: TUnitInfo read GetPrevAutoRevertLockedUnit;
property NextPartOfProject: TUnitInfo read GetNextPartOfProject;
property PrevPartOfProject: TUnitInfo read GetPrevPartOfProject;
public
property Bookmarks: TFileBookmarks read FBookmarks write FBookmarks;
property BuildFileIfActive: boolean read GetBuildFileIfActive
write SetBuildFileIfActive;
property Component: TComponent read fComponent write SetComponent;
property ComponentName: string read fComponentName write fComponentName;
property ComponentResourceName: string read fComponentResourceName
write fComponentResourceName;
property ComponentTypesToClasses: TStringToPointerTree read FComponentTypesToClasses
write FComponentTypesToClasses; // classname to TComponentClass, for not registered and ambiguous classes in lfm
property ComponentVarsToClasses: TStringToPointerTree read FComponentVarsToClasses
write FComponentVarsToClasses; // variablename to TComponentClass, for ambiguous classes in lfm
property ComponentState: TWindowState read FComponentState write FComponentState;
property ResourceBaseClass: TPFComponentBaseClass read FResourceBaseClass
write FResourceBaseClass;
property ResourceBaseClassname: string read FResourceBaseClassname write FResourceBaseClassname;
property ComponentLastBinStreamSize: TStreamSeekType
read FComponentLastBinStreamSize write FComponentLastBinStreamSize;
property ComponentLastLRSStreamSize: TStreamSeekType
read FComponentLastLRSStreamSize write FComponentLastLRSStreamSize;
property ComponentLastLFMStreamSize: TStreamSeekType
read FComponentLastLFMStreamSize write FComponentLastLFMStreamSize;
property CustomDefaultHighlighter: boolean
read GetCustomDefaultHighlighter write SetCustomDefaultHighlighter;
property Directives: TStrings read FDirectives write FDirectives;
property DisableI18NForLFM: boolean read GetDisableI18NForLFM write SetDisableI18NForLFM;
property FileReadOnly: Boolean read GetFileReadOnly write SetFileReadOnly;
property FirstRequiredComponent: TUnitComponentDependency read FFirstRequiredComponent;
property FirstUsedByComponent: TUnitComponentDependency read FFirstUsedByComponent;
property Flags: TUnitInfoFlags read FFlags;
property HasErrorInLFM: boolean read GetHasErrorInLFM write SetHasErrorInLFM;
property HasResources: boolean read GetHasResources write SetHasResources;
property InternalFile: boolean read GetInternalFile write SetInternalFile;
property Loaded: Boolean read GetLoaded write SetLoaded;
property LoadedDesigner: Boolean read GetLoadedDesigner write SetLoadedDesigner;
property LoadingComponent: boolean read GetLoadingComponent write SetLoadingComponent;
property Modified: boolean read GetModified write SetModified;// not Session data
property SessionModified: boolean read GetSessionModified write SetSessionModified;
property OnFileBackup: TOnFileBackup read fOnFileBackup write fOnFileBackup;
property OnLoadSaveFilename: TOnLoadSaveFilename
read fOnLoadSaveFilename write fOnLoadSaveFilename;
property OnUnitNameChange: TOnUnitNameChange
read FOnUnitNameChange write FOnUnitNameChange;
property Project: TProject read FProject write SetProject;
property RunFileIfActive: boolean read GetRunFileIfActive write SetRunFileIfActive;
property Source: TCodeBuffer read fSource write SetSource;
property SourceLFM: TCodeBuffer read FSourceLFM write SetSourceLFM;
property DefaultSyntaxHighlighter: TIdeSyntaxHighlighterID
read FDefaultSyntaxHighlighter write SetDefaultSyntaxHighlighter;
property UserReadOnly: Boolean read GetUserReadOnly write SetUserReadOnly;
property AutoReferenceSourceDir: boolean read GetAutoReferenceSourceDir
write SetAutoReferenceSourceDir;
end;
//---------------------------------------------------------------------------
{ TProjectCompilationToolOptions }
TProjectCompilationToolOptions = class(TCompilationToolOptions)
private
FDefaultCompileReasons: TCompileReasons;
procedure SetDefaultCompileReasons(const AValue: TCompileReasons);
protected
procedure SetCompileReasons(const AValue: TCompileReasons); override;
procedure SubstituteMacros(var s: string); override;
public
constructor Create(TheOwner: TLazCompilerOptions); override;
function CreateDiff(CompOpts: TCompilationToolOptions;
Tool: TCompilerDiffTool): boolean; override;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
DoSwitchPathDelims: boolean); override;
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
UsePathDelim: TPathDelimSwitch); override;
function GetProject: TProject;
public
property DefaultCompileReasons: TCompileReasons read FDefaultCompileReasons write SetDefaultCompileReasons;
end;
TProjectBuildMode = class;
{ TProjectCompilerOptions }
TProjectCompilerOptions = class(TBaseCompilerOptions)
private
FBuildMode: TProjectBuildMode;
FProject: TProject;
FCompileReasons: TCompileReasons;
procedure BeforeReadExec(Sender: TObject);
procedure AfterWriteExec(Sender: TObject; Restore: boolean);
protected
procedure SetTargetCPU(const AValue: string); override;
procedure SetTargetOS(const AValue: string); override;
procedure SetCustomOptions(const AValue: string); override;
procedure SetIncludePaths(const AValue: string); override;
procedure SetLibraryPaths(const AValue: string); override;
procedure SetLinkerOptions(const AValue: string); override;
procedure SetNamespaces(const AValue: string); override;
procedure SetObjectPath(const AValue: string); override;
procedure SetSrcPath(const AValue: string); override;
procedure SetUnitPaths(const AValue: string); override;
procedure SetUnitOutputDir(const AValue: string); override;
procedure SetConditionals(AValue: string); override;
function SubstituteProjectMacros(s: string;
PlatformIndependent: boolean): string;
public
constructor Create(const AOwner: TObject); override;
destructor Destroy; override;
function IsActive: boolean; override;
class function GetInstance: TAbstractIDEOptions; override;
class function GetGroupCaption: string; override;
procedure Clear; override;
procedure LoadFromXMLConfig(AXMLConfig: TXMLConfig; const Path: string); override;
procedure SaveToXMLConfig(AXMLConfig: TXMLConfig; const Path: string); override;
function CanBeDefaulForProject: boolean; override;
function GetOwnerName: string; override;
function GetDefaultMainSourceFileName: string; override;
function GetDefaultWriteConfigFilePath: string; override;
procedure GetInheritedCompilerOptions(var OptionsList: TFPList); override;
procedure Assign(Source: TPersistent); override;
function CreateDiff(CompOpts: TBaseCompilerOptions;
Tool: TCompilerDiffTool = nil): boolean; override; // true if differ
procedure SetAlternativeCompile(const Command: string; ScanFPCMsgs: boolean); override;
public
property LazProject: TProject read FProject;
property BuildMode: TProjectBuildMode read FBuildMode;
published
property CompileReasons: TCompileReasons read FCompileReasons write FCompileReasons;
end;
{ TProjectDefineTemplates }
TProjectDefineTemplates = class(TProjPackDefineTemplates)
private
procedure FixTemplateOrder;
protected
procedure UpdateMain; override;
function UpdateSrcDirIfDef: Boolean; override;
procedure UpdateSourceDirectories; override;
procedure UpdateOutputDirectory; override;
procedure UpdateDefinesForCustomDefines; override;
procedure ClearFlags; override;
public
constructor Create(AOwner: IProjPack);
destructor Destroy; override;
procedure AllChanged(AActivating: boolean); override;
procedure UpdateGlobalValues;
end;
{ TProjectBuildMode }
TProjectBuildMode = class(TLazProjectBuildMode)
private
FCompilerOptions: TProjectCompilerOptions;
protected
function GetLazCompilerOptions: TLazCompilerOptions; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function LazProject: TProject;
procedure Clear;
function Equals(Src: TProjectBuildMode): boolean; reintroduce;
function CreateDiff(Other: TProjectBuildMode;
Tool: TCompilerDiffTool = nil): boolean;
procedure Assign(Src: TProjectBuildMode); reintroduce;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveMacroValuesAtOldPlace(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
IsDefault, ALegacyList: Boolean; var Cnt: integer);
function GetCaption: string; override;
function GetIndex: integer; override;
public
// copied by Assign, compared by Equals, cleared by Clear
property CompilerOptions: TProjectCompilerOptions read FCompilerOptions;
end;
{ TProjectBuildModes }
TProjectBuildModes = class(TLazProjectBuildModes)
private
FAssigning: Boolean;
FSessionMatrixOptions: TBuildMatrixOptions;
FSharedMatrixOptions: TBuildMatrixOptions;
FManyBuildModes: TStringList; // User selection of many modes.
fSavedChangeStamp: int64;
fItems: TFPList;
FLazProject: TProject;
fChangedHandlers: TMethodList;
// Variables used by LoadFromXMLConfig and SaveToXMLConfig
FXMLConfig: TXMLConfig;
FGlobalMatrixOptions: TBuildMatrixOptions;
function GetItems(Index: integer): TProjectBuildMode;
function GetModified: boolean;
procedure ItemChanged(Sender: TObject);
procedure SetModified(const AValue: boolean);
// Used by LoadFromXMLConfig
procedure AddMatrixMacro(const MacroName, MacroValue, ModeIdentifier: string; InSession: boolean);
procedure LoadSessionEnabledNonSessionMatrixOptions(const Path: string);
procedure LoadOtherCompilerOpts(const Path: string; FromIndex, ToIndex: Integer; InSession: boolean);
procedure LoadMacroValues(const Path: string; CurMode: TProjectBuildMode);
procedure LoadAllMacroValues(const Path: string; Cnt: Integer);
procedure LoadOldFormat(const Path: string);
procedure LoadActiveBuildMode(const Path: string);
// Used by SaveToXMLConfig
procedure SaveSessionData(const Path: string);
procedure SaveSharedMatrixOptions(const Path: string);
protected
function GetLazBuildModes(Index: integer): TLazProjectBuildMode; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
function IsEqual(OtherModes: TProjectBuildModes): boolean;
procedure Assign(Source: TPersistent; WithModified: boolean); overload;
procedure Delete(Index: integer);
function IndexOf(Identifier: string): integer;
function IndexOf(aMode: TProjectBuildMode): integer;
function Find(Identifier: string): TProjectBuildMode;
function Add(Identifier: string): TProjectBuildMode;
procedure Move(FromIndex, ToIndex: integer);
function Count: integer; override;
procedure IncreaseChangeStamp;
procedure AddOnChangedHandler(const Handler: TNotifyEvent);
procedure RemoveOnChangedHandler(const Handler: TNotifyEvent);
function IsModified(InSession: boolean): boolean;
function GetSessionModes: TStringList;
function IsSessionMode(const ModeIdentifier: string): boolean;
function IsSharedMode(const ModeIdentifier: string): boolean;
procedure RenameMatrixMode(const OldName, NewName: string);
function CreateExtraModes(aCurMode: TProjectBuildMode): TProjectBuildMode;
// load, save
procedure LoadProjOptsFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadSessionFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
LoadAllOptions: boolean);
procedure SaveProjOptsToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveSession, ALegacyList: boolean);
procedure SaveSessionOptsToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveSession, ALegacyList: boolean);
public
property Items[Index: integer]: TProjectBuildMode read GetItems; default;
property ChangeStamp: integer read FChangeStamp;
property LazProject: TProject read FLazProject write FLazProject;
property Assigning: Boolean read FAssigning;
property Modified: boolean read GetModified write SetModified;
property SharedMatrixOptions: TBuildMatrixOptions read FSharedMatrixOptions;
property SessionMatrixOptions: TBuildMatrixOptions read FSessionMatrixOptions;
property ManyBuildModes: TStringList read FManyBuildModes;
property ChangedHandlers: TMethodList read fChangedHandlers;
end;
{ TProjectIDEOptions }
TProjectIDEOptions = class(TAbstractIDEProjectOptions)
private
FProject: TProject;
FLclApp: Boolean;
public
constructor Create(AProject: TProject);
destructor Destroy; override;
function GetProject: TLazProject; override;
function CheckLclApp: Boolean;
class function GetInstance: TAbstractIDEOptions; override;
class function GetGroupCaption: string; override;
property Project: TProject read FProject;
property LclApp: Boolean read FLclApp;
end;
TProjectDebugLinkBase = class
private
protected
procedure Clear; virtual; abstract;
procedure BeforeReadProject; virtual; abstract;
procedure AfterReadProject; virtual; abstract;
procedure LoadFromLPI(aXMLConfig: TRttiXMLConfig; Path: string); virtual; abstract;
procedure LoadFromSession(aXMLConfig: TRttiXMLConfig; Path: string); virtual; abstract;
procedure SaveToLPI(aXMLConfig: TRttiXMLConfig; Path: string); virtual; abstract;
procedure SaveToSession(aXMLConfig: TRttiXMLConfig; Path: string); virtual; abstract;
public
//constructor Create;
//destructor Destroy; override;
end;
{ TProject }
TEndUpdateProjectEvent =
procedure(Sender: TObject; ProjectChanged: boolean) of object;
TLazProjectStateFlag = (
lpsfStateFileLoaded,
lpsfPropertyDependenciesChanged,
lpsfDesignerChanged
);
TLazProjectStateFlags = set of TLazProjectStateFlag;
TOldProjectType = (ptApplication, ptProgram, ptCustomProgram);
TProject = class(TLazProject, IProjPack)
private
FActiveBuildMode: TProjectBuildMode;
FActiveBuildModeBackup: integer;
FActiveWindowIndexAtStart: integer;
FBuildModes: TProjectBuildModes;
FBuildModesBackup: TProjectBuildModes;
FAllEditorsInfoList: TUnitEditorInfoList;
FAllEditorsInfoMap: TMap;
FAutoCreateForms: boolean;
FDebuggerLink: TProjectDebugLinkBase;
FChangeStampSaved: integer;
FEnableI18NForLFM: boolean;
FHistoryLists: THistoryLists;
FLastCompileComplete: boolean;
FMacroEngine: TTransferMacroList;
FOnLoadSafeCustomData: TLazLoadSaveCustomDataEvent;
FTmpAutoCreatedForms: TStrings; // temporary, used to apply auto create forms changes
FAutoOpenDesignerFormsDisabled: boolean;
FBookmarks: TProjectBookmarkList;
fChanged: boolean;
fCurStorePathDelim: TPathDelimSwitch; // used by OnLoadSaveFilename
FDefineTemplates: TProjectDefineTemplates;
fDestroying: boolean;
FEnableI18N: boolean;
FI18NExcludedIdentifiers: TStrings;
FI18NExcludedOriginals: TStrings;
FForceUpdatePoFiles: Boolean;
fFirst, fLast: array[TUnitInfoList] of TUnitInfo;
FFirstRemovedDependency: TPkgDependency;
FFirstRequiredDependency: TPkgDependency;
FJumpHistory: TProjectJumpHistory;
FLastCompilerFileDate: integer;
FLastCompilerFilename: string;
FLastCompilerParams: TStrings;
fLastReadLPIFileDate: TDateTime;
fLastReadLPIFilename: string;
FLockUnitComponentDependencies: integer;
FMainProject: boolean;
fMainUnitID: Integer;
FOnBeginUpdate: TNotifyEvent;
FOnChangeProjectInfoFile: TOnChangeProjectInfoFile;
FOnEndUpdate: TEndUpdateProjectEvent;
fOnFileBackup: TOnFileBackup;
FOnLoadProjectInfo: TOnLoadProjectInfo;
FOnSaveProjectInfo: TOnSaveProjectInfo;
FOnSaveUnitSessionInfo: TOnSaveUnitSessionInfoInfo;
fPathDelimChanged: boolean; // PathDelim in system and current config differ (see StorePathDelim and SessionStorePathDelim)
FPOOutputDirectory: string;
fProjectDirectory: string;
fProjectDirectoryReferenced: string;
fProjectInfoFile: String; // the lpi filename
fProjectInfoFileBuffer: TCodeBuffer;
fProjectInfoFileBufChangeStamp: integer;
fProjectInfoFileDate: LongInt;
FPublishOptions: TPublishProjectOptions;
FRevertLockCount: integer;
FSessionModifiedBackup: boolean;
FSessionStorePathDelim: TPathDelimSwitch;
FSkipCheckLCLInterfaces: boolean;
FSourceDirectories: TFileReferenceList;
FStateFileDate: longint;
FStateFlags: TLazProjectStateFlags;
FStorePathDelim: TPathDelimSwitch;
FUnitList: TFPList; // list of _all_ units (TUnitInfo)
FOtherDefines: TStrings; // list of user selectable defines for custom options
FUpdateLock: integer;
FUseAsDefault: Boolean;
// Variables used by ReadProject / WriteProject
FXMLConfig: TRttiXMLConfig;
FLoadAllOptions: Boolean; // All options / just options used as default for new projects
FFileVersion: Integer;
FNewMainUnitID: LongInt;
FProjectWriteFlags: TProjectWriteFlags;
FSaveSessionInLPI: Boolean;
procedure ClearBuildModes;
function GetAllEditorsInfo(Index: Integer): TUnitEditorInfo;
function GetCompilerOptions: TProjectCompilerOptions;
function GetBaseCompilerOptions: TBaseCompilerOptions;
function GetFirstAutoRevertLockedUnit: TUnitInfo;
function GetFirstLoadedUnit: TUnitInfo;
function GetFirstPartOfProject: TUnitInfo;
function GetFirstUnitWithComponent: TUnitInfo;
function GetFirstUnitWithEditorIndex: TUnitInfo;
function GetIDEOptions: TProjectIDEOptions;
function GetMainFilename: String;
function GetMainUnitInfo: TUnitInfo;
function GetProjResources: TProjectResources;
function GetRunParameterOptions: TRunParamsOptions;
function GetSourceDirectories: TFileReferenceList;
function GetTargetFilename: string;
function GetUnits(Index: integer): TUnitInfo;
function GetUseLegacyLists: Boolean;
function JumpHistoryCheckPosition(
APosition:TProjectJumpHistoryPosition): boolean;
procedure ClearSourceDirectories;
procedure EmbeddedObjectModified(Sender: TObject);
function FileBackupHandler(const Filename: string): TModalResult;
procedure LoadSaveFilenameHandler(var AFilename: string; Load: boolean);
procedure UnitNameChangeHandler(AnUnitInfo: TUnitInfo;
const OldUnitName, NewUnitName: string;
CheckIfAllowed: boolean; var Allowed: boolean);
procedure SetActiveBuildMode(const AValue: TProjectBuildMode);
procedure SetAutoOpenDesignerFormsDisabled(const AValue: boolean);
procedure SetEnableI18N(const AValue: boolean);
procedure SetEnableI18NForLFM(const AValue: boolean);
procedure SetLastCompilerParams(AValue: TStrings);
procedure SetMainProject(const AValue: boolean);
procedure SetMainUnitID(const AValue: Integer);
procedure SetPOOutputDirectory(const AValue: string);
procedure SetSkipCheckLCLInterfaces(const AValue: boolean);
procedure SetStorePathDelim(const AValue: TPathDelimSwitch);
procedure SetTargetFilename(const NewTargetFilename: string);
procedure SourceDirectoriesChanged(Sender: TObject);
function UnitNameExists(const AnUnitName: string): boolean;
procedure UpdateFileBuffer;
procedure UpdateProjectDirectory;
procedure UpdateSessionFilename;
procedure UpdateSourceDirectories;
procedure UpdateUsageCounts(const ConfigFilename: string);
function UnitMustBeSaved(UnitInfo: TUnitInfo; WriteFlags: TProjectWriteFlags;
SaveSession: boolean): boolean;
procedure UpdateVisibleEditor(PgIndex: integer);
procedure LoadDefaultSession;
procedure EditorInfoAdd(EdInfo: TUnitEditorInfo);
procedure EditorInfoRemove(EdInfo: TUnitEditorInfo);
procedure MacroEngineSubstitution({%H-}TheMacro: TTransferMacro;
const MacroName: string; var s: string;
const Data: PtrInt; var Handled, Abort: boolean; Depth: integer);
// Methods for ReadProject
function LoadOldProjectType(const Path: string): TOldProjectType;
procedure LoadFlags(const Path: string);
procedure LoadOtherDefines(const Path: string);
procedure LoadCustomData(Sender: TObject; Data: TStringToStringTree;
XMLConfig: TXMLConfig; const Path: string);
procedure LoadSessionInfo(const Path: string; Merge: boolean);
procedure LoadFromLPI;
procedure LoadFromSession;
function DoLoadLPI(Filename: String): TModalResult;
function DoLoadSession(Filename: String): TModalResult;
function DoLoadLPR(Revert: boolean): TModalResult;
// Methods for WriteProject
procedure SaveFlags(const Path: string);
procedure SaveUnits(const Path: string; SaveSession: boolean);
procedure SaveOtherDefines(const Path: string);
procedure SaveCustomData(Sender: TObject; Data: TStringToStringTree;
XMLConfig: TXMLConfig; const Path: string);
procedure SaveSessionInfo(const Path: string);
procedure SaveToLPI;
procedure SaveToSession;
function DoWrite(Filename: String; IsLpi: Boolean): TModalResult;
protected
function GetDirectory: string; override;
function GetActiveBuildModeID: string; override;
function GetDefineTemplates: TProjPackDefineTemplates;
function GetFiles(Index: integer): TLazProjectFile; override;
function GetLazBuildModes: TLazProjectBuildModes; override;
function GetMainFile: TLazProjectFile; override;
function GetMainFileID: Integer; override;
function GetModified: boolean; override;
function GetProjectInfoFile: string; override;
function GetUseManifest: boolean; override;
procedure SetActiveBuildModeID(aIdent: string); override;
procedure SetExecutableType(const AValue: TProjectExecutableType); override;
procedure SetFlags(const AValue: TProjectFlags); override;
procedure SetMainFileID(const AValue: Integer); override;
procedure SetModified(const AValue: boolean); override;
procedure SetProjectInfoFile(const NewFilename: string); override;
procedure SetSessionModified(const AValue: boolean); override;
procedure SetSessionStorage(const AValue: TProjectSessionStorage); override;
procedure SetUseManifest(AValue: boolean); override;
//function GetCurrentDebuggerBackend: String; override;
protected
// special unit lists
procedure AddToList(AnUnitInfo: TUnitInfo; ListType: TUnitInfoList);
procedure RemoveFromList(AnUnitInfo: TUnitInfo; ListType: TUnitInfoList);
procedure AddToOrRemoveFromAutoRevertLockedList(AnUnitInfo: TUnitInfo);
procedure AddToOrRemoveFromComponentList(AnUnitInfo: TUnitInfo);
procedure AddToOrRemoveFromLoadedList(AnUnitInfo: TUnitInfo);
procedure AddToOrRemoveFromPartOfProjectList(AnUnitInfo: TUnitInfo);
public
constructor Create(ProjectDescription: TProjectDescriptor); override;
destructor Destroy; override;
procedure Clear; override;
procedure BeginUpdate(Change: boolean);
procedure EndUpdate;
procedure UnitModified(AnUnitInfo: TUnitInfo);
function NeedsDefineTemplates: boolean;
procedure BeginRevertUnit(AnUnitInfo: TUnitInfo);
procedure EndRevertUnit(AnUnitInfo: TUnitInfo);
function IsLclApplication: Boolean;
function IsReverting(AnUnitInfo: TUnitInfo): boolean;
function IsVirtual: boolean; override;
// load/save
function SomethingModified(CheckData, CheckSession: boolean; Verbose: boolean = false): boolean;
function SomeDataModified(Verbose: boolean = false): boolean;
function SomeSessionModified(Verbose: boolean = false): boolean;
procedure MainSourceFilenameChanged;
procedure GetSourcesChangedOnDisk(var ACodeBufferList: TFPList);
function HasProjectInfoFileChangedOnDisk: boolean;
procedure IgnoreProjectInfoFileOnDisk;
function ReadProject(const NewProjectInfoFile: string;
GlobalMatrixOptions: TBuildMatrixOptions;
LoadAllOptions: Boolean = True): TModalResult;
function WriteProject(ProjectWriteFlags: TProjectWriteFlags;
const OverrideProjectInfoFile: string;
GlobalMatrixOptions: TBuildMatrixOptions): TModalResult;
procedure UpdateExecutableType; override;
procedure BackupSession;
procedure RestoreSession;
procedure BackupBuildModes;
procedure RestoreBuildModes;
// title
function GetTitle: string; override;
function TitleIsDefault(Fuzzy: boolean = false): boolean;
function GetIDAsString: string;
function GetIDAsWord: string;
// units
function UnitCount:integer;
function GetFileCount: integer; override;
function NewUniqueUnitName(const AnUnitName: string): string;
function NewUniqueFilename(const Filename: string): string;
procedure AddFile(ProjectFile: TLazProjectFile;
AddToProjectUsesClause: boolean); override;
procedure RemoveUnit(Index: integer;
RemoveFromUsesSection: boolean = true); override;
// true if something changed
function RemoveNonExistingFiles(RemoveFromUsesSection: boolean = true): boolean;
function CreateProjectFile(const Filename: string): TLazProjectFile; override;
function GetAndUpdateVisibleUnit(AnEditor: TSourceEditorInterface;
AWindowID: Integer): TUnitInfo;
procedure UpdateAllVisibleUnits;
// search
function IndexOf(AUnitInfo: TUnitInfo): integer;
function IndexOfUnitWithName(const AnUnitName: string;
OnlyProjectUnits:boolean; IgnoreUnit: TUnitInfo): integer;
function IndexOfUnitWithComponent(AComponent: TComponent;
OnlyProjectUnits:boolean; IgnoreUnit: TUnitInfo): integer;
function IndexOfUnitWithComponentName(const AComponentName: string;
OnlyProjectUnits:boolean; IgnoreUnit: TUnitInfo): integer;
function IndexOfFilename(const AFilename: string): integer;
function IndexOfFilename(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): integer;
function IndexOfLFMFilename(const AFilename: string): integer; // only currently open lfm (SourceLFM<>nil)
function ProjectUnitWithFilename(const AFilename: string): TUnitInfo;
function ProjectUnitWithShortFilename(const ShortFilename: string): TUnitInfo;
function ProjectUnitWithUnitname(const AnUnitName: string): TUnitInfo;
function UnitWithEditorComponent(AEditor:TSourceEditorInterface): TUnitInfo;
function UnitWithComponent(AComponent: TComponent): TUnitInfo;
function UnitWithComponentClass(AClass: TComponentClass): TUnitInfo;
function UnitWithComponentClassName(const AClassName: string): TUnitInfo;
function UnitWithComponentName(AComponentName: String;
OnlyPartOfProject: boolean): TUnitInfo;
function UnitComponentInheritingFrom(AClass: TComponentClass;
Ignore: TUnitInfo): TUnitInfo;
function UnitUsingComponentUnit(ComponentUnit: TUnitInfo;
Types: TUnitCompDependencyTypes): TUnitInfo;
function UnitComponentIsUsed(ComponentUnit: TUnitInfo;
CheckHasDesigner: boolean): boolean;
function UnitInfoWithFilename(const AFilename: string): TUnitInfo;
function UnitInfoWithFilename(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): TUnitInfo;
function UnitWithUnitname(const AnUnitname: string): TUnitInfo;
function UnitInfoWithLFMFilename(const AFilename: string): TUnitInfo; // only currently open lfm (SourceLFM<>nil)
function AllEditorsInfoCount: Integer;
property AllEditorsInfo[Index: Integer]: TUnitEditorInfo read GetAllEditorsInfo;
function EditorInfoWithEditorComponent({%H-}AEditor:TSourceEditorInterface): TUnitEditorInfo;
function SearchFile(const ShortFilename: string;
SearchFlags: TSearchIDEFileFlags): TUnitInfo;
function FindFile(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): TLazProjectFile; override;
// used units with 'in' modifier
function UpdateIsPartOfProjectFromMainUnit: TModalResult;
// Application.CreateForm statements
function AddCreateFormToProjectFile(const AClassName, AName:string): boolean;
function RemoveCreateFormFromProjectFile(const AName: string): boolean;
function FormIsCreatedInProjectFile(const AClassname, AName:string): boolean;
function GetAutoCreatedFormsList: TStrings;
property TmpAutoCreatedForms: TStrings read FTmpAutoCreatedForms write FTmpAutoCreatedForms;
// resources
function GetMainResourceFilename(AnUnitInfo: TUnitInfo): string;
function GetResourceFile(AnUnitInfo: TUnitInfo; Index:integer):TCodeBuffer;
procedure LoadDefaultIcon; override;
// filenames and fileinfo
function RemoveProjectPathFromFilename(const AFilename: string): string;
function FileIsInProjectDir(const AFilename: string): boolean;
procedure GetVirtualDefines(DefTree: TDefineTree; DirDef: TDirectoryDefines);
function GetShortFilename(const Filename: string; UseUp: boolean): string; override;
procedure ConvertToLPIFilename(var AFilename: string); override;
procedure ConvertFromLPIFilename(var AFilename: string); override;
// package dependencies
function FindDependencyByName(const PackageName: string): TPkgDependency;
function FindRemovedDependencyByName(const PkgName: string): TPkgDependency;
function RequiredDepByIndex(Index: integer): TPkgDependency;
function RemovedDepByIndex(Index: integer): TPkgDependency;
procedure AddRequiredDependency(Dependency: TPkgDependency);
procedure RemoveRequiredDependency(Dependency: TPkgDependency);
procedure DeleteRequiredDependency(Dependency: TPkgDependency);
procedure DeleteRemovedDependency(Dependency: TPkgDependency);
procedure RemoveRemovedDependency(Dependency: TPkgDependency);
procedure ReaddRemovedDependency(Dependency: TPkgDependency);
procedure MoveRequiredDependencyUp(Dependency: TPkgDependency);
procedure MoveRequiredDependencyDown(Dependency: TPkgDependency);
function Requires(APackage: TLazPackage; SearchRecursively: boolean): boolean;
procedure GetAllRequiredPackages(var List: TFPList;
ReqFlags: TPkgIntfRequiredFlags = [];
MinPolicy: TPackageUpdatePolicy = low(TPackageUpdatePolicy));
procedure AddPackageDependency(const PackageName: string); override;
function RemovePackageDependency(const PackageName: string): boolean;
override;
// unit dependencies
procedure LockUnitComponentDependencies;
procedure UnlockUnitComponentDependencies;
procedure UpdateUnitComponentDependencies;
procedure InvalidateUnitComponentDesignerDependencies;
procedure ClearUnitComponentDependencies(ClearTypes: TUnitCompDependencyTypes);
procedure FindUnitsUsingSubComponent(SubComponent: TComponent;
List: TFPList; IgnoreOwner: boolean);
procedure WriteDebugReportUnitComponentDependencies(Prefix: string);
// paths
procedure AddSrcPath(const SrcPathAddition: string); override;
function GetSourceDirs(WithProjectDir, WithoutOutputDir: boolean): string;
function GetOutputDirectory: string;
function GetCompilerFilename: string;
function GetStateFilename: string;
function GetCompileSourceFilename: string;
function GetWriteConfigFilePath: string;
procedure AutoAddOutputDirToIncPath;
function ExtendUnitSearchPath(NewUnitPaths: string): boolean;
function ExtendIncSearchPath(NewIncPaths: string): boolean;
// compile state file
function LoadStateFile(IgnoreErrors: boolean): TModalResult;
function SaveStateFile(const CompilerFilename: string; CompilerParams: TStrings;
Complete: boolean): TModalResult;
// source editor
procedure UpdateAllCustomHighlighter;
procedure UpdateAllSyntaxHighlighter;
// i18n
function GetPOOutDirectory: string;
// bookmarks
function AddBookmark(X, Y, ID: Integer; AUnitInfo:TUnitInfo):integer;
procedure DeleteBookmark(ID: Integer);
public
property ActiveBuildMode: TProjectBuildMode read FActiveBuildMode
write SetActiveBuildMode;
property ActiveWindowIndexAtStart: integer read FActiveWindowIndexAtStart
write FActiveWindowIndexAtStart;
property AutoCreateForms: boolean read FAutoCreateForms write FAutoCreateForms; // add CreateForm for new forms
property AutoOpenDesignerFormsDisabled: boolean read FAutoOpenDesignerFormsDisabled
write SetAutoOpenDesignerFormsDisabled;
property Bookmarks: TProjectBookmarkList read FBookmarks write FBookmarks;
property BuildModes: TProjectBuildModes read FBuildModes;
property SkipCheckLCLInterfaces: boolean read FSkipCheckLCLInterfaces
write SetSkipCheckLCLInterfaces;
property CompilerOptions: TProjectCompilerOptions read GetCompilerOptions;
property DebuggerLink: TProjectDebugLinkBase read FDebuggerLink write FDebuggerLink;
property DefineTemplates: TProjectDefineTemplates read FDefineTemplates;
property Destroying: boolean read fDestroying;
property EnableI18N: boolean read FEnableI18N write SetEnableI18N;
property EnableI18NForLFM: boolean read FEnableI18NForLFM write SetEnableI18NForLFM;
property I18NExcludedIdentifiers: TStrings read FI18NExcludedIdentifiers;
property I18NExcludedOriginals: TStrings read FI18NExcludedOriginals;
property UseLegacyLists: Boolean read GetUseLegacyLists;
property ForceUpdatePoFiles: Boolean read FForceUpdatePoFiles write FForceUpdatePoFiles;
property FirstAutoRevertLockedUnit: TUnitInfo read GetFirstAutoRevertLockedUnit;
property FirstLoadedUnit: TUnitInfo read GetFirstLoadedUnit;
property FirstPartOfProject: TUnitInfo read GetFirstPartOfProject;
property FirstRemovedDependency: TPkgDependency read FFirstRemovedDependency;
property FirstRequiredDependency: TPkgDependency read FFirstRequiredDependency;
property FirstUnitWithComponent: TUnitInfo read GetFirstUnitWithComponent;
property FirstUnitWithEditorIndex: TUnitInfo read GetFirstUnitWithEditorIndex;
property IDAsString: string read GetIDAsString;
property IDAsWord: string read GetIDAsWord;
property IDEOptions: TProjectIDEOptions read GetIDEOptions;
property JumpHistory: TProjectJumpHistory read FJumpHistory write FJumpHistory;
property LastCompilerFileDate: integer read FLastCompilerFileDate
write FLastCompilerFileDate;
property LastCompilerFilename: string read FLastCompilerFilename
write FLastCompilerFilename;
property LastCompilerParams: TStrings read FLastCompilerParams
write SetLastCompilerParams;
property LastCompileComplete: boolean read FLastCompileComplete write FLastCompileComplete;
property MacroEngine: TTransferMacroList read FMacroEngine;
property MainFilename: String read GetMainFilename;
property MainProject: boolean read FMainProject write SetMainProject;
property MainUnitID: Integer read FMainUnitID write SetMainUnitID;
property MainUnitInfo: TUnitInfo read GetMainUnitInfo;
property OnBeginUpdate: TNotifyEvent read FOnBeginUpdate write FOnBeginUpdate;
property OnChangeProjectInfoFile: TOnChangeProjectInfoFile read FOnChangeProjectInfoFile
write FOnChangeProjectInfoFile;
property OnEndUpdate: TEndUpdateProjectEvent read FOnEndUpdate write FOnEndUpdate;
property OnFileBackup: TOnFileBackup read fOnFileBackup write fOnFileBackup;
property OnLoadProjectInfo: TOnLoadProjectInfo read FOnLoadProjectInfo
write FOnLoadProjectInfo;
property OnSaveProjectInfo: TOnSaveProjectInfo read FOnSaveProjectInfo
write FOnSaveProjectInfo;
property OnSaveUnitSessionInfo: TOnSaveUnitSessionInfoInfo
read FOnSaveUnitSessionInfo write FOnSaveUnitSessionInfo;
property OnLoadSafeCustomData: TLazLoadSaveCustomDataEvent read FOnLoadSafeCustomData write FOnLoadSafeCustomData;
property POOutputDirectory: string read FPOOutputDirectory write SetPOOutputDirectory;
property PublishOptions: TPublishProjectOptions read FPublishOptions write FPublishOptions;
property ProjResources: TProjectResources read GetProjResources;
property RunParameterOptions: TRunParamsOptions read GetRunParameterOptions;
property HistoryLists: THistoryLists read FHistoryLists;
property SourceDirectories: TFileReferenceList read GetSourceDirectories;
property StateFileDate: longint read FStateFileDate write FStateFileDate;
property StateFlags: TLazProjectStateFlags read FStateFlags write FStateFlags;
property SessionStorePathDelim: TPathDelimSwitch read FSessionStorePathDelim write FSessionStorePathDelim;
property StorePathDelim: TPathDelimSwitch read FStorePathDelim write SetStorePathDelim;
property TargetFilename: string read GetTargetFilename write SetTargetFilename;
property Units[Index: integer]: TUnitInfo read GetUnits;
property OtherDefines: TStrings read FOtherDefines;
property UpdateLock: integer read FUpdateLock;
property UseAsDefault: Boolean read FUseAsDefault write FUseAsDefault; // for dialog only (used to store options once)
end;
const
ResourceFileExt = '.lrs';
DefaultProjectOptionsFilename = 'projectoptions.xml';
DefaultProjectCompilerOptionsFilename = 'compileroptions.xml'; // old way < 0.9.31
OldProjectTypeNames : array[TOldProjectType] of string = (
'Application', 'Program', 'Custom program'
);
var
Project1: TProject absolute LazProject1;// the main project
function FilenameToLazSyntaxHighlighter(Filename: String): TIdeSyntaxHighlighterID;
function AddCompileReasonsDiff(const PropertyName: string;
const Old, New: TCompileReasons; Tool: TCompilerDiffTool = nil): boolean;
function dbgs(aType: TUnitCompDependencyType): string; overload;
function dbgs(Types: TUnitCompDependencyTypes): string; overload;
function dbgs(Flag: TUnitInfoFlag): string; overload;
function dbgs(Flags: TUnitInfoFlags): string; overload;
implementation
const
ProjectInfoFileVersion = 12;
ProjOptionsPath = 'ProjectOptions/';
function FilenameToLazSyntaxHighlighter(Filename: String): TIdeSyntaxHighlighterID;
var
CompilerMode: TCompilerMode;
begin
CompilerMode:=CodeToolBoss.GetCompilerModeForDirectory(ExtractFilePath(Filename));
Result := IdeSyntaxHighlighters.GetIdForFileExtension(ExtractFileExt(Filename), CompilerMode in [cmDELPHI,cmTP]);
end;
function AddCompileReasonsDiff(const PropertyName: string;
const Old, New: TCompileReasons; Tool: TCompilerDiffTool): boolean;
begin
if Old=New then exit(false);
Result:=true;
Tool.AddSetDiff(PropertyName,integer(Old),integer(New),
PString(@CompileReasonNames[Low(TCompileReasons)]));
end;
function dbgs(aType: TUnitCompDependencyType): string;
begin
case aType of
ucdtAncestor: Result:='Ancestor';
ucdtProperty: Result:='Property';
ucdtOldProperty: Result:='OldProperty';
ucdtInlineClass: Result:='InlineClass';
else Result:='?'
{%H-}end;
end;
function dbgs(Types: TUnitCompDependencyTypes): string;
var
t: TUnitCompDependencyType;
begin
Result:='';
for t:=low(Types) to High(Types) do
if t in Types then begin
if Result<>'' then Result:=Result+';';
Result:=Result+dbgs(t);
end;
Result:='['+Result+']';
end;
function dbgs(Flag: TUnitInfoFlag): string;
begin
Result:='';
WriteStr(Result, Flag);
end;
function dbgs(Flags: TUnitInfoFlags): string;
var
f: TUnitInfoFlag;
begin
Result:='';
for f:=low(Flags) to High(Flags) do
if f in Flags then begin
if Result<>'' then Result:=Result+';';
Result:=Result+dbgs(f);
end;
Result:='['+Result+']';
end;
{ TUnitEditorInfo }
procedure TUnitEditorInfo.SetEditorComponent(const AValue: TSourceEditorInterface);
begin
if FEditorComponent = AValue then exit;
if AValue = nil then begin
fUnitInfo.Project.FAllEditorsInfoMap.Delete(FEditorComponent);
FEditorComponent := AValue;
UnitInfo.FEditorInfoList.MakeUnUsedEditorInfo(Self);
PageIndex := -1; // calls UnitInfo.UpdatePageIndex
IsLocked := False;
end
else begin
PageIndex := -1;
with fUnitInfo.Project do // Map for lookup: Editor -> EditorInfo
if not FAllEditorsInfoMap.HasId(AValue) then
FAllEditorsInfoMap.Add(AValue, Self);
FEditorComponent := AValue;
UnitInfo.FEditorInfoList.MakeUsedEditorInfo(Self);
AValue.UpdateProjectFile; // Set EditorIndex / calls UnitInfo.UpdatePageIndex
end;
FUnitInfo.SessionModified:=true;
end;
procedure TUnitEditorInfo.SetPageIndex(const AValue: Integer);
begin
if FPageIndex = AValue then exit;
FPageIndex := AValue;
FUnitInfo.UpdatePageIndex;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetFoldState(AValue: String);
begin
if FFoldState = AValue then Exit;
FFoldState := AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetIsLocked(const AValue: Boolean);
begin
if FIsLocked=AValue then Exit;
FIsLocked:=AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetCursorPos(const AValue: TPoint);
begin
if ComparePoints(FCursorPos,AValue)=0 then Exit;
FCursorPos:=AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetIsVisibleTab(const AValue: Boolean);
begin
if FIsVisibleTab = AValue then exit;
FIsVisibleTab := AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetSyntaxHighlighter(AValue: TIdeSyntaxHighlighterID);
begin
if FSyntaxHighlighter = AValue then Exit;
FSyntaxHighlighter := AValue;
FCustomHighlighter := FSyntaxHighlighter <> FUnitInfo.DefaultSyntaxHighlighter;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetTopLine(const AValue: Integer);
begin
if FTopLine=AValue then Exit;
FTopLine:=AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.SetWindowIndex(const AValue: Integer);
begin
if FWindowID = AValue then exit;
FWindowID := AValue;
FUnitInfo.SessionModified := True;
end;
procedure TUnitEditorInfo.Clear;
begin
FIsVisibleTab := False;
FPageIndex := -1;
FWindowID := -1;
FTopLine := -1;
FCursorPos.X := -1;
FCursorPos.Y := -1;
FFoldState := '';
FSyntaxHighlighter := FUnitInfo.DefaultSyntaxHighlighter;
FCustomHighlighter := FUnitInfo.CustomDefaultHighlighter;
end;
constructor TUnitEditorInfo.Create(aUnitInfo: TUnitInfo);
begin
FUnitInfo := aUnitInfo;
Clear;
if FUnitInfo.Project <> nil then
FUnitInfo.Project.EditorInfoAdd(Self);
end;
destructor TUnitEditorInfo.Destroy;
begin
if FUnitInfo.Project <> nil then
FUnitInfo.Project.EditorInfoRemove(Self);
inherited Destroy;
end;
procedure TUnitEditorInfo.LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
begin
IsVisibleTab := XMLConfig.GetValue(Path+'IsVisibleTab/Value', False);
FPageIndex := XMLConfig.GetValue(Path+'EditorIndex/Value',0);
WindowID := XMLConfig.GetValue(Path+'WindowIndex/Value',0);
// update old data
if (FPageIndex >= 0) and (FWindowID < 0) then
WindowID := 1;
FTopLine := XMLConfig.GetValue(Path+'TopLine/Value',1);
FCursorPos := Point(XMLConfig.GetValue(Path+'CursorPos/X',1),
XMLConfig.GetValue(Path+'CursorPos/Y',1));
FFoldState := XMLConfig.GetValue(Path+'FoldState/Value', '');
FIsLocked := XMLConfig.GetValue(Path+'IsLocked/Value', False);
if IdeSyntaxHighlighters <> nil then
FSyntaxHighlighter := IdeSyntaxHighlighters.GetIdForName(
XMLConfig.GetValue(Path+'SyntaxHighlighter/Value',
IdeSyntaxHighlighters.Names[UnitInfo.DefaultSyntaxHighlighter]));
end;
procedure TUnitEditorInfo.SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveFold: Boolean);
begin
XMLConfig.SetDeleteValue(Path+'IsVisibleTab/Value', FIsVisibleTab, False);
XMLConfig.SetDeleteValue(Path+'EditorIndex/Value', FPageIndex, 0);
XMLConfig.SetDeleteValue(Path+'WindowIndex/Value', FWindowID, 0);
XMLConfig.SetDeleteValue(Path+'TopLine/Value', FTopLine, 1);
XMLConfig.SetDeleteValue(Path+'CursorPos/X', FCursorPos.X, 1);
XMLConfig.SetDeleteValue(Path+'CursorPos/Y', FCursorPos.Y, 1);
XMLConfig.SetDeleteValue(Path+'IsLocked/Value', FIsLocked, False);
if SaveFold then
XMLConfig.SetDeleteValue(Path+'FoldState/Value', FoldState, '')
else
XMLConfig.DeletePath(Path+'FoldState');
if IdeSyntaxHighlighters <> nil then
XMLConfig.SetDeleteValue(Path+'SyntaxHighlighter/Value',
IdeSyntaxHighlighters.Names[fSyntaxHighlighter],
IdeSyntaxHighlighters.Names[UnitInfo.DefaultSyntaxHighlighter]);
end;
{ TUnitEditorInfoList }
function TUnitEditorInfoList.GetEditorInfos(Index: Integer): TUnitEditorInfo;
begin
Result := TUnitEditorInfo(FList[Index]);
end;
function TUnitEditorInfoList.GetClosedEditorInfos(Index: Integer): TUnitEditorInfo;
var
i: Integer;
begin
i := 0;
while (i < Count) and (Index >= 0) do begin
Result := EditorInfos[i];
if Result.EditorComponent = nil then dec(Index);
inc(i);
end;
if Index >= 0 then
Result := nil;
end;
function TUnitEditorInfoList.GetOpenEditorInfos(Index: Integer): TUnitEditorInfo;
var
i: Integer;
begin
i := 0;
while (i < Count) and (Index >= 0) do begin
Result := EditorInfos[i];
if Result.EditorComponent <> nil then dec(Index);
inc(i);
end;
if Index >= 0 then
Result := nil;
end;
procedure TUnitEditorInfoList.ClearEachInfo;
var
i: Integer;
begin
for i := 0 to Count - 1 do
EditorInfos[i].Clear;
end;
function CompareEditorInfoByPageIndex(EditorInfo1, EditorInfo2: TUnitEditorInfo): integer;
begin
Result := EditorInfo1.WindowID - EditorInfo2.WindowID;
if Result = 0 then
Result := EditorInfo1.PageIndex - EditorInfo2.PageIndex;
end;
procedure TUnitEditorInfoList.SortByPageIndex;
begin
FList.Sort(TListSortCompare(@CompareEditorInfoByPageIndex));
end;
procedure TUnitEditorInfoList.SetLastUsedEditor(AEditor: TSourceEditorInterface);
var
i: Integer;
begin
i := IndexOfEditorComponent(AEditor);
if i <> 0 then
FList.Move(i, 0);
end;
procedure TUnitEditorInfoList.MakeUsedEditorInfo(AEditorInfo: TUnitEditorInfo);
var
i, j: Integer;
begin
i := FList.IndexOf(AEditorInfo);
j := OpenCount;
if (i > j) and (j < Count) then
FList.Move(i, j);
end;
procedure TUnitEditorInfoList.MakeUnUsedEditorInfo(AEditorInfo: TUnitEditorInfo);
var
i: Integer;
begin
i := FList.IndexOf(AEditorInfo);
if i <> FList.Count - 1 then
FList.Move(i, FList.Count - 1);
end;
procedure TUnitEditorInfoList.Clear;
begin
while Count > 0 do begin
EditorInfos[0].Free;
Delete(0);
end;
end;
constructor TUnitEditorInfoList.Create(aUnitInfo: TUnitInfo);
begin
FUnitInfo := aUnitInfo;
FList := TFPList.Create;
end;
destructor TUnitEditorInfoList.Destroy;
begin
Clear;
FreeAndNil(FList);
inherited Destroy;
end;
function TUnitEditorInfoList.Count: Integer;
begin
Result := FList.Count;
end;
function TUnitEditorInfoList.OpenCount: Integer;
var
i: Integer;
begin
i := Count - 1;
Result := 0;
while i >= 0 do begin
if EditorInfos[i].EditorComponent <> nil then inc(Result);
dec(i);
end;
end;
function TUnitEditorInfoList.ClosedCount: Integer;
var
i: Integer;
begin
i := Count - 1;
Result := 0;
while i >= 0 do begin
if EditorInfos[i].EditorComponent = nil then inc(Result);
dec(i);
end;
end;
function TUnitEditorInfoList.IndexOfEditorComponent(anEditor: TSourceEditorInterface): Integer;
begin
Result := Count - 1;
while (Result >= 0) and (EditorInfos[Result].EditorComponent <> anEditor) do
dec(Result);
end;
function TUnitEditorInfoList.NewEditorInfo: TUnitEditorInfo;
begin
Result := TUnitEditorInfo.Create(FUnitInfo);
FList.Add(Result);
end;
procedure TUnitEditorInfoList.Add(AEditorInfo: TUnitEditorInfo);
begin
FList.Add(AEditorInfo);
end;
procedure TUnitEditorInfoList.Delete(Index: Integer);
begin
Flist.Delete(Index);
end;
procedure TUnitEditorInfoList.Remove(AEditorInfo: TUnitEditorInfo);
var
i: LongInt;
begin
i := FList.IndexOf(AEditorInfo);
if i >= 0 then
Delete(i);
end;
{------------------------------------------------------------------------------
TUnitInfo Constructor
------------------------------------------------------------------------------}
constructor TUnitInfo.Create(ACodeBuffer: TCodeBuffer);
begin
inherited Create;
//DebugLn('Trace:Project Unit Info Class Created');
FEditorInfoList := TUnitEditorInfoList.Create(Self);
FEditorInfoList.NewEditorInfo;
FBookmarks:=TFileBookmarks.Create;
Clear;
Source := ACodeBuffer;
if Source=nil then
FFileName:='';
end;
{------------------------------------------------------------------------------
TUnitInfo Destructor
------------------------------------------------------------------------------}
destructor TUnitInfo.Destroy;
begin
Component:=nil;
Source:=nil;
FreeAndNil(FBookmarks);
Project:=nil;
FreeAndNil(FEditorInfoList);
FreeAndNil(FComponentTypesToClasses);
FreeAndNil(FComponentVarsToClasses);
inherited Destroy;
end;
function TUnitInfo.GetFileOwner: TObject;
begin
Result:=Project;
end;
function TUnitInfo.GetFileOwnerName: string;
begin
if Project<>nil then
Result:=ExtractFilename(Project.ProjectInfoFile)
else
Result:='';
end;
{------------------------------------------------------------------------------
TUnitInfo WriteUnitSource
------------------------------------------------------------------------------}
function TUnitInfo.WriteUnitSource: TModalResult;
var
ACaption:string;
AText:string;
begin
if fSource=nil then
exit(mrOK);
if Assigned(fOnFileBackup) then begin
Result:=fOnFileBackup(Filename);
if Result=mrAbort then exit;
end;
repeat
if fSource.Save then begin
Result:=mrOk;
end else begin
ACaption:=lisCodeToolsDefsWriteError;
AText:=Format(lisUnableToWriteFile2, [Filename]);
Result:=IDEMessageDialog(ACaption,AText,mtError,mbAbortRetryIgnore);
if Result=mrAbort then exit;
if Result=mrIgnore then Result:=mrOk;
end;
until Result<>mrRetry;
Result:=mrOk;
end;
function TUnitInfo.WriteUnitSourceToFile(const AFileName: string): TModalResult;
var
ACaption:string;
AText:string;
begin
if fSource=nil then
exit(mrOK);
if Assigned(fOnFileBackup) then begin
Result:=fOnFileBackup(AFilename);
if Result=mrAbort then exit;
end;
repeat
if not fSource.SaveToFile(AFileName) then begin
ACaption:=lisCodeToolsDefsWriteError;
AText:=Format(lisUnableToWriteFile2, [AFilename]);
Result:=IDEMessageDialog(ACaption,AText,mtError,mbAbortRetryIgnore);
if Result=mrAbort then exit;
if Result=mrIgnore then Result:=mrOk;
end else
Result:=mrOk;
until Result<>mrRetry;
Result:=mrOk;
end;
{------------------------------------------------------------------------------
TUnitInfo ReadUnitSource
------------------------------------------------------------------------------}
function TUnitInfo.ReadUnitSource(ReadUnitName,Revert:boolean): TModalResult;
var
ACaption:string;
AText:string;
NewSource: TCodeBuffer;
begin
repeat
NewSource:=CodeToolBoss.LoadFile(Filename,true,Revert);
if NewSource=nil then begin
ACaption:=lisCodeToolsDefsReadError;
AText:=Format(lisUnableToReadFile2, [Filename]);
Result:=IDEMessageDialog(ACaption,AText,mtError,mbAbortRetryIgnore);
if Result in [mrAbort,mrIgnore] then
exit;
end else begin
Source:=NewSource;
Result:=mrOk;
end;
until Result<>mrRetry;
if ReadUnitName then begin
ReadUnitNameFromSource(false);
end;
Result:=mrOk;
end;
function TUnitInfo.ReadUnitNameFromSource(TryCache: boolean): string;
begin
Result:='';
if TryCache then
Result:=CodeToolBoss.GetCachedSourceName(Source);
if Result='' then
Result:=CodeToolBoss.GetSourceName(fSource,false);
if Result<>'' then begin
// source can be parsed => update UnitName
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.ReadUnitNameFromSource ',Result]);
{$ENDIF}
FUnitName:=Result;
end else begin
// unable to parse the source
if FilenameIsPascalSource(Filename) then begin
// use default: the filename
Result:=ExtractFileNameOnly(Filename);
if CompareText(Result,FUnitName)=0 then begin
// the last stored unitname has the better case
Result:=FUnitName;
end;
end;
end;
end;
function TUnitInfo.GetUsesUnitName: string;
begin
if FilenameHasPascalExt(Filename) then
begin
if FUnitName<>'' then
Result:=FUnitName
else
Result:=ExtractFileNameOnly(Filename);
end
else
Result:='';
end;
function TUnitInfo.CreateUnitName: string;
begin
Result:=FUnitName;
if (Result='') and FilenameIsPascalSource(Filename) then
Result:=ExtractFilenameOnly(Filename);
end;
{------------------------------------------------------------------------------
TUnitInfo Clear
------------------------------------------------------------------------------}
procedure TUnitInfo.Clear;
begin
FBookmarks.Clear;
FSetBookmarLock := 0;
BuildFileIfActive:=false;
fComponent := nil;
fComponentName := '';
fComponentResourceName := '';
FComponentState := wsNormal;
FDefaultSyntaxHighlighter := IdeHighlighterNoneID;
DisableI18NForLFM:=false;
CustomDefaultHighlighter := False;
FEditorInfoList.ClearEachInfo;
fFilename := '';
FileReadOnly := false;
HasResources := false;
AutoReferenceSourceDir := true;
inherited SetIsPartOfProject(false);
Modified := false;
SessionModified := false;
RunFileIfActive:=false;
FUnitName := '';
fUsageCount:=-1;
UserReadOnly := false;
if fSource<>nil then fSource.Clear;
Loaded := false;
LoadedDesigner := false;
ClearComponentDependencies;
end;
procedure TUnitInfo.ClearModifieds;
begin
Modified:=false;
SessionModified:=false;
end;
procedure TUnitInfo.ClearComponentDependencies;
begin
while FFirstRequiredComponent<>nil do FFirstRequiredComponent.Free;
while FFirstUsedByComponent<>nil do FFirstUsedByComponent.Free;
SourceLFM:=nil;
end;
procedure TUnitInfo.WriteDebugReportUnitComponentDependencies(Prefix: string);
var
Dependency: TUnitComponentDependency;
begin
DebugLn([Prefix+'TUnitInfo.WriteDebugReportUnitComponentDependencies ',Filename,' ',dbgs(Flags)]);
Dependency:=FirstRequiredComponent;
if Dependency<>nil then begin
DebugLn([Prefix+' Requires: >>> ']);
while Dependency<>nil do begin
DebugLn([Prefix+' '+Dependency.RequiresUnit.Filename+' '+dbgs(Dependency.Types)]);
Dependency:=Dependency.NextRequiresDependency;
end;
end;
Dependency:=FirstUsedByComponent;
if Dependency<>nil then begin
DebugLn([Prefix+' UsedBy: <<<']);
while Dependency<>nil do begin
DebugLn([Prefix+' '+Dependency.UsedByUnit.Filename+' '+dbgs(Dependency.Types)]);
Dependency:=Dependency.NextUsedByDependency;
end;
end;
end;
{------------------------------------------------------------------------------
TUnitInfo SaveToXMLConfig
------------------------------------------------------------------------------}
procedure TUnitInfo.SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveData, SaveSession: boolean; UsePathDelim: TPathDelimSwitch);
var
AFilename: String;
i, X, Y: Integer;
s: String;
BM: TFileBookmark;
begin
// global data
AFilename:=Filename;
if Assigned(fOnLoadSaveFilename) then
fOnLoadSaveFilename(AFilename, False);
XMLConfig.SetValue(Path+'Filename/Value',SwitchPathDelims(AFilename,UsePathDelim));
if SaveData then
XMLConfig.SetDeleteValue(Path+'IsPartOfProject/Value',IsPartOfProject,false);
if SaveSession and Assigned(Project.OnSaveUnitSessionInfo) then
Project.OnSaveUnitSessionInfo(Self);
if IsPartOfProject and SaveData then
XMLConfig.SetDeleteValue(Path+'DisableI18NForLFM/Value',DisableI18NForLFM,false);
// context data (project/session)
if (IsPartOfProject and SaveData) or ((not IsPartOfProject) and SaveSession)
then begin
XMLConfig.SetDeleteValue(Path+'ComponentName/Value',fComponentName,'');
XMLConfig.SetDeleteValue(Path+'HasResources/Value',uifHasResources in FFlags,false);
XMLConfig.SetDeleteValue(Path+'ResourceBaseClass/Value',
PFComponentBaseClassNames[FResourceBaseClass],
PFComponentBaseClassNames[pfcbcNone]);
XMLConfig.SetDeleteValue(Path+'ResourceBaseClassname/Value',
FResourceBaseClassname,
DefaultResourceBaseClassnames[FResourceBaseClass]);
s:=FUnitName;
if (s<>'') and (ExtractFileNameOnly(Filename)=s) then s:=''; // only save if UnitName differs from filename
XMLConfig.SetDeleteValue(Path+'UnitName/Value',s,'');
// save custom data
Project.SaveCustomData(Self,CustomData,XMLConfig,Path+'CustomData/');
end;
// session data
if SaveSession then
begin
FEditorInfoList[0].SaveToXMLConfig(XMLConfig, Path, pfSaveFoldState in Project.Flags);
XMLConfig.SetDeleteValue(Path+'ExtraEditorCount/Value', FEditorInfoList.Count-1, 0);
for i := 1 to FEditorInfoList.Count - 1 do
FEditorInfoList[i].SaveToXMLConfig(XMLConfig, Path + 'ExtraEditor'+IntToStr(i)+'/',
pfSaveFoldState in Project.Flags);
XMLConfig.SetDeleteValue(Path+'ComponentState/Value',Ord(FComponentState),0);
XMLConfig.SetDeleteValue(Path+'UsageCount/Value',RoundToInt(fUsageCount),-1);
if OpenEditorInfoCount > 0 then
for i := Bookmarks.Count - 1 downto 0 do
begin
BM := Bookmarks[i];
if (Project.Bookmarks.BookmarkWithID(BM.ID) = nil) or
(Project.Bookmarks.BookmarkWithID(BM.ID).UnitInfo <> self)
then
Bookmarks.Delete(i)
else
if OpenEditorInfo[0].EditorComponent.GetBookMark(BM.ID, X, Y) then
BM.CursorPos := Point(X, Y);
end;
FBookmarks.SaveToXMLConfig(XMLConfig,Path+'Bookmarks/');
XMLConfig.SetDeleteValue(Path+'Loaded/Value',Loaded,false);
XMLConfig.SetDeleteValue(Path+'LoadedDesigner/Value',LoadedDesigner,false);
XMLConfig.SetDeleteValue(Path+'ReadOnly/Value',UserReadOnly,false);
XMLConfig.SetDeleteValue(Path+'BuildFileIfActive/Value',
BuildFileIfActive,false);
XMLConfig.SetDeleteValue(Path+'RunFileIfActive/Value',
RunFileIfActive,false);
// save custom session data
SaveStringToStringTree(XMLConfig,CustomSessionData,Path+'CustomSessionData/');
if IdeSyntaxHighlighters <> nil then
XMLConfig.SetDeleteValue(Path+'DefaultSyntaxHighlighter/Value',
IdeSyntaxHighlighters.Names[FDefaultSyntaxHighlighter],
IdeSyntaxHighlighters.Names[IdeSyntaxHighlighters.GetIdForLazSyntaxHighlighter(lshFreePascal)]);
end;
end;
{------------------------------------------------------------------------------
TUnitInfo LoadFromXMLConfig
------------------------------------------------------------------------------}
procedure TUnitInfo.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string; Merge, IgnoreIsPartOfProject: boolean;
FileVersion: integer);
var
AFilename: string;
c, i: Integer;
begin
// project data
if not Merge then begin
AFilename:=XMLConfig.GetValue(Path+'Filename/Value','');
if Assigned(fOnLoadSaveFilename) then
fOnLoadSaveFilename(AFilename,true);
fFilename:=AFilename;
fComponentName:=XMLConfig.GetValue(Path+'ComponentName/Value','');
if fComponentName='' then
fComponentName:=XMLConfig.GetValue(Path+'FormName/Value','');
FComponentState := TWindowState(XMLConfig.GetValue(Path+'ComponentState/Value',0));
DisableI18NForLFM:=XMLConfig.GetValue(Path+'DisableI18NForLFM/Value',false);
HasResources:=XMLConfig.GetValue(Path+'HasResources/Value',false);
FResourceBaseClass:=StrToComponentBaseClass(
XMLConfig.GetValue(Path+'ResourceBaseClass/Value',''));
FResourceBaseClassname:=XMLConfig.GetValue(Path+'ResourceBaseClassname/Value',
DefaultResourceBaseClassnames[FResourceBaseClass]);
if not IgnoreIsPartOfProject then
IsPartOfProject:=XMLConfig.GetValue(Path+'IsPartOfProject/Value',false);
AFilename:=XMLConfig.GetValue(Path+'ResourceFilename/Value','');
if (AFilename<>'') and Assigned(fOnLoadSaveFilename) then
fOnLoadSaveFilename(AFilename,true);
if FilenameIsPascalSource(Filename) then begin
FUnitName:=XMLConfig.GetValue(Path+'UnitName/Value','');
if FUnitName='' then
FUnitName:=ExtractFileNameOnly(Filename);
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.LoadFromXMLConfig ',FUnitName]);
{$ENDIF}
end else
FUnitName:='';
// save custom data
Project.LoadCustomData(Self,CustomData,XMLConfig,Path+'CustomData/');
end;
// session data
if IdeSyntaxHighlighters <> nil then
FDefaultSyntaxHighlighter := IdeSyntaxHighlighters.GetIdForName(
XMLConfig.GetValue(Path+'DefaultSyntaxHighlighter/Value',
IdeSyntaxHighlighters.Names[IdeSyntaxHighlighters.GetIdForLazSyntaxHighlighter(lshFreePascal)]));
FEditorInfoList.Clear;
FEditorInfoList.NewEditorInfo;
FEditorInfoList[0].LoadFromXMLConfig(XMLConfig, Path);
c := XMLConfig.GetValue(Path+'ExtraEditorCount/Value', 0);
for i := 1 to c do
FEditorInfoList.NewEditorInfo.LoadFromXMLConfig(XMLConfig, Path + 'ExtraEditor'+IntToStr(i)+'/');
UpdatePageIndex;
Loaded:=XMLConfig.GetValue(Path+'Loaded/Value',false);
if Loaded then
LoadedDesigner:=XMLConfig.GetValue(Path+'LoadedDesigner/Value',FileVersion<8)
else
LoadedDesigner:=false;
UserReadOnly:=XMLConfig.GetValue(Path+'ReadOnly/Value',false);
BuildFileIfActive:=XMLConfig.GetValue(Path+'BuildFileIfActive/Value',false);
RunFileIfActive:=XMLConfig.GetValue(Path+'RunFileIfActive/Value',false);
fUsageCount:=XMLConfig.GetValue(Path+'UsageCount/Value',-1);
if fUsageCount<1 then begin
UpdateUsageCount(uuIsLoaded,1);
if IsPartOfProject then
UpdateUsageCount(uuIsPartOfProject,1);
end;
FBookmarks.LoadFromXMLConfig(XMLConfig,Path+'Bookmarks/');
// load custom session data
LoadStringToStringTree(XMLConfig,CustomSessionData,Path+'CustomSessionData/');
end;
procedure TUnitInfo.SetUnitName(const AValue: string);
var
Allowed: boolean;
OldUnitName: String;
begin
if (FUnitName <> AValue) and (AValue <> '') then
begin
Allowed := true;
OldUnitName := FUnitName;
if OldUnitName = '' then
OldUnitName := ExtractFileNameOnly(Filename);
if Assigned(FOnUnitNameChange) then
FOnUnitNameChange(Self, OldUnitName, AValue, false, Allowed);
// (ignore Allowed)
if (fSource <> nil) then
begin
CodeToolBoss.RenameSource(fSource,AValue);
end;
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.SetSrcUnitName ',AValue]);
{$ENDIF}
FUnitName := AValue;
Modified := true;
if (Project <> nil) then Project.UnitModified(Self);
end;
end;
procedure TUnitInfo.UpdateList(ListType: TUnitInfoList; Add: boolean);
begin
if Project<>nil then begin
if Add then
Project.AddToList(Self,ListType)
else
Project.RemoveFromList(Self,ListType);
end else begin
fNext[ListType]:=nil;
fPrev[ListType]:=nil;
end;
end;
procedure TUnitInfo.SetInternalFilename(const NewFilename: string);
begin
if fFileName=NewFilename then exit;
//DebugLn('TUnitInfo.SetInternalFilename Old=',fFileName,' New=',NewFilename);
// if directory changed then remove the old directory reference
if FSourceDirectoryReferenced
and (Project<>nil)
and (fLastDirectoryReferenced<>GetDirectory) then begin
Project.SourceDirectories.RemoveFilename(fLastDirectoryReferenced);
FSourceDirectoryReferenced:=false;
end;
fFileName:=NewFilename;
if IDEEditorOptions<>nil then
UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(FFilename));
UpdateSourceDirectoryReference;
end;
procedure TUnitInfo.UpdateHasCustomHighlighter(
aDefaultHighlighter: TIdeSyntaxHighlighterID);
var
i: Integer;
begin
CustomDefaultHighlighter := FDefaultSyntaxHighlighter <> aDefaultHighlighter;
for i := 0 to FEditorInfoList.Count - 1 do
FEditorInfoList[i].CustomHighlighter :=
FEditorInfoList[i].SyntaxHighlighter <> aDefaultHighlighter;
end;
procedure TUnitInfo.UpdatePageIndex;
var
HasPageIndex: Boolean;
i, j: integer;
BM: TFileBookmark;
begin
HasPageIndex := False;
i := FEditorInfoList.Count - 1;
while (i >= 0) and not HasPageIndex do begin
if EditorInfo[i].PageIndex >= 0 then
HasPageIndex := True;
dec(i);
end;
UpdateList(uilWithEditorIndex, HasPageIndex);
if Assigned(Project1) and Assigned(Project1.Bookmarks) then
begin
if OpenEditorInfoCount > 0 then begin
inc(FSetBookmarLock);
try
// Adjust bookmarks
for i := Bookmarks.Count-1 downto 0 do
begin
BM := Bookmarks[i];
j := Project1.Bookmarks.IndexOfID(BM.ID);
if (j < 0) then
OpenEditorInfo[0].EditorComponent.SetBookMark(BM.ID, BM.CursorPos.X, BM.CursorPos.Y);
end;
finally
dec(FSetBookmarLock);
end;
end
else // OpenEditorInfoCount = 0
Project1.Bookmarks.DeleteAllWithUnitInfo(Self);
end;
end;
procedure TUnitInfo.UpdateDefaultHighlighter(
aDefaultHighlighter: TIdeSyntaxHighlighterID);
var
i: Integer;
begin
//debugln(['TUnitInfo.UpdateDefaultHighlighter ',Filename,' ',ord(aDefaultHighlighter)]);
if not CustomDefaultHighlighter then
DefaultSyntaxHighlighter := aDefaultHighlighter
else
for i := 0 to FEditorInfoList.Count - 1 do
if not FEditorInfoList[i].CustomHighlighter then
FEditorInfoList[i].SyntaxHighlighter := aDefaultHighlighter;
end;
function TUnitInfo.GetFileName: string;
begin
if fSource<>nil then
Result:=fSource.Filename
else
Result:=fFileName;
end;
procedure TUnitInfo.SetFilename(const AValue: string);
begin
if fSource<>nil then
RaiseGDBException('TUnitInfo.SetFilename Source<>nil')
else
SetInternalFilename(AValue);
end;
function TUnitInfo.IsVirtual: boolean;
begin
if fSource<>nil then
Result:=fSource.IsVirtual
else
Result:=not FilenameIsAbsolute(fFileName);
end;
function TUnitInfo.GetDirectory: string;
begin
if IsVirtual then begin
if Project<>nil then
Result:=Project.Directory
else
Result:='';
end else begin
Result:=ExtractFilePath(Filename);
end;
end;
function TUnitInfo.GetFullFilename: string;
begin
Result:=fFilename;
// not saved files have file names without path
// they exist in the Codetools filename space
end;
function TUnitInfo.GetShortFilename(UseUp: boolean): string;
begin
if Project<>nil then
Result:=Project.GetShortFilename(Filename,UseUp)
else
Result:=Filename;
end;
function TUnitInfo.IsMainUnit: boolean;
begin
Result:=(Project<>nil) and (Project.MainUnitInfo=Self);
end;
procedure TUnitInfo.IncreaseAutoRevertLock;
begin
inc(fAutoRevertLockCount);
if fAutoRevertLockCount=1 then begin
// activate lock
if Source<>nil then
Source.LockAutoDiskRevert;
if Project<>nil then
Project.AddToOrRemoveFromAutoRevertLockedList(Self);
end;
end;
procedure TUnitInfo.DecreaseAutoRevertLock;
begin
dec(fAutoRevertLockCount);
if fAutoRevertLockCount=0 then begin
// deactivate lock
if Source<>nil then
Source.LockAutoDiskRevert;
if Project<>nil then
Project.AddToOrRemoveFromAutoRevertLockedList(Self);
end;
end;
function TUnitInfo.IsAutoRevertLocked: boolean;
begin
Result:=fAutoRevertLockCount>0;
end;
function TUnitInfo.IsReverting: boolean;
begin
Result:=FRevertLockCount>0;
end;
function TUnitInfo.ComponentLFMOnDiskHasChanged: boolean;
// Associated LFM resource file on disk has changed since last load/save
begin
if SourceLFM=nil then Exit(false);
if SourceLFM.FileOnDiskHasChanged then exit(true);
end;
function TUnitInfo.GetAutoReferenceSourceDir: boolean;
begin
Result:=uifAutoReferenceSourceDir in FFlags;
end;
function TUnitInfo.GetBuildFileIfActive: boolean;
begin
Result:=uifBuildFileIfActive in FFlags;
end;
function TUnitInfo.GetCustomDefaultHighlighter: boolean;
begin
Result:=uifCustomDefaultHighlighter in FFlags;
end;
function TUnitInfo.GetDisableI18NForLFM: boolean;
begin
Result:=uifDisableI18NForLFM in FFlags;
end;
procedure TUnitInfo.SetTimeStamps;
begin
if FSource<>nil then
fSourceChangeStep:=FSource.ChangeStep // Indicates any change is source
else
fSourceChangeStep:=LUInvalidChangeStamp;
end;
function TUnitInfo.IsChangedOnDisk(CheckLFM: boolean): boolean;
begin
Result:=(Source<>nil) and Source.FileOnDiskHasChanged;
if (not Result) and CheckLFM and (Component<>nil) and (SourceLFM<>nil) then
Result:=SourceLFM.FileOnDiskHasChanged;
FileReadOnly:=(not IsVirtual) and FileExistsCached(Filename)
and not FileIsWritableCached(Filename);
end;
procedure TUnitInfo.IgnoreCurrentFileDateOnDisk;
begin
if Source<>nil then
Source.MakeFileDateValid;
if SourceLFM<>nil then
SourceLFM.MakeFileDateValid;
end;
function TUnitInfo.ShortFilename: string;
begin
if Project<>nil then
Result:=Project.RemoveProjectPathFromFilename(Filename)
else
Result:=Filename;
end;
function TUnitInfo.NeedsSaveToDisk(CheckLFM: boolean): boolean;
begin
// Modified has a test for Source.ChangeStep<>fSourceChangeStep.
Result:=IsVirtual or Modified or IsChangedOnDisk(CheckLFM and FilenameIsPascalSource(Filename));
//DebugLn(['TUnitInfo.NeedsSaveToDisk ',Filename,' Result=',Result,' Modified=',Modified]);
end;
procedure TUnitInfo.UpdateUsageCount(Min, IfBelowThis, IncIfBelow: extended);
begin
if fUsageCount<IfBelowThis then fUsageCount:=fUsageCount+IncIfBelow;
if fUsageCount<Min then fUsageCount:=Min;
end;
procedure TUnitInfo.UpdateUsageCount(TheUsage: TUnitUsage; const Factor: TDateTime);
begin
case TheUsage of
uuIsPartOfProject: UpdateUsageCount(20,200,2*Factor);
uuIsLoaded: UpdateUsageCount(10,100,1*Factor);
uuIsModified: UpdateUsageCount(10,0,0);
uuNotUsed: fUsageCount:=fUsageCount-(Factor/5);
end;
end;
procedure TUnitInfo.UpdateSourceDirectoryReference;
begin
if (not AutoReferenceSourceDir) or (FProject=nil) then exit;
if IsPartOfProject and (FilenameIsPascalSource(Filename)) then begin
if not FSourceDirectoryReferenced then begin
fLastDirectoryReferenced:=GetDirectory;
//DebugLn('TUnitInfo.UpdateSourceDirectoryReference ADD File="',Filename,'" Project.SourceDirectories.TimeStamp=',dbgs(Project.SourceDirectories.TimeStamp));
FSourceDirectoryReferenced:=true;
Project.SourceDirectories.AddFilename(fLastDirectoryReferenced);
end;
end else begin
if FSourceDirectoryReferenced then begin
//DebugLn('TUnitInfo.UpdateSourceDirectoryReference REMOVE File="',Filename,'" Project.SourceDirectories.TimeStamp=',dbgs(Project.SourceDirectories.TimeStamp));
FSourceDirectoryReferenced:=false;
Project.SourceDirectories.RemoveFilename(fLastDirectoryReferenced);
end;
end;
end;
procedure TUnitInfo.SetSourceText(const SourceText: string; Beautify: boolean);
var
Src: String;
begin
Src:=SourceText;
if Beautify then
Src:=SourceEditorManagerIntf.Beautify(Src);
Source.Source:=Src;
end;
function TUnitInfo.GetSourceText: string;
begin
Result:=Source.Source;
end;
function TUnitInfo.AddRequiresComponentDependency(RequiredUnit: TUnitInfo;
Types: TUnitCompDependencyTypes): TUnitComponentDependency;
begin
if RequiredUnit=nil then RaiseGDBException('inconsistency');
if RequiredUnit=Self then RaiseGDBException('inconsistency');
// search a dependency to this RequiredUnit
Result:=FirstRequiredComponent;
while Result<>nil do begin
if Result.RequiresUnit=RequiredUnit then break;
Result:=Result.NextRequiresDependency;
end;
// if none exists, then create one
if Result=nil then begin
Result:=TUnitComponentDependency.Create;
Result.UsedByUnit:=Self;
Result.RequiresUnit:=RequiredUnit;
end;
Result.Types:=Result.Types+Types;
end;
procedure TUnitInfo.RemoveRequiresComponentDependency(RequiredUnit: TUnitInfo;
Types: TUnitCompDependencyTypes);
var
Dependency: TUnitComponentDependency;
NextDependency: TUnitComponentDependency;
begin
Dependency:=FirstRequiredComponent;
while Dependency<>nil do begin
NextDependency:=Dependency.NextRequiresDependency;
if (Dependency.RequiresUnit=RequiredUnit) then begin
Dependency.Types:=Dependency.Types-Types;
if Dependency.Types=[] then
Dependency.Free;
end;
Dependency:=NextDependency;
end;
end;
function TUnitInfo.FindComponentDependency(RequiredUnit: TUnitInfo
): TUnitComponentDependency;
begin
Result:=FirstRequiredComponent;
while Result<>nil do begin
if Result.RequiresUnit=RequiredUnit then exit;
Result:=Result.NextRequiresDependency;
end;
end;
function TUnitInfo.FindRequiredComponentDependency(
MinTypes: TUnitCompDependencyTypes): TUnitComponentDependency;
begin
Result:=FirstRequiredComponent;
while Result<>nil do begin
if Result.Types*MinTypes=MinTypes then exit;
Result:=Result.NextRequiresDependency;
end;
end;
function TUnitInfo.FindUsedByComponentDependency(
MinTypes: TUnitCompDependencyTypes): TUnitComponentDependency;
begin
Result:=FirstUsedByComponent;
while Result<>nil do begin
if Result.Types*MinTypes=MinTypes then exit;
Result:=Result.NextUsedByDependency;
end;
end;
function TUnitInfo.FindAncestorUnit: TUnitInfo;
var
Dependency: TUnitComponentDependency;
begin
if Component<>nil then begin
Dependency:=FirstRequiredComponent;
while Dependency<>nil do begin
Result:=Dependency.RequiresUnit;
if (Result.Component<>nil)
and (Component.ClassParent=Result.Component.ClassType) then
exit;
Dependency:=Dependency.NextRequiresDependency;
end;
end;
Result:=nil;
end;
procedure TUnitInfo.ClearUnitComponentDependencies(
ClearTypes: TUnitCompDependencyTypes);
var
Dep: TUnitComponentDependency;
NextDep: TUnitComponentDependency;
begin
Dep:=FirstRequiredComponent;
while Dep<>nil do begin
NextDep:=Dep.NextRequiresDependency;
Dep.Types:=Dep.Types-ClearTypes;
if Dep.Types=[] then
Dep.Free;
Dep:=NextDep;
end;
end;
function TUnitInfo.AddBookmark(X, Y, ID: integer): integer;
begin
if FSetBookmarLock = 0 then
Result := Bookmarks.Add(X, Y, ID)
else
Result := -1;
SessionModified := True;
Project1.AddBookmark(X, Y, ID, Self);
end;
procedure TUnitInfo.DeleteBookmark(ID: integer);
var
i: Integer;
begin
i := Bookmarks.IndexOfID(ID);
if i >= 0 then begin
Bookmarks.Delete(i);
SessionModified := True;
end;
Project1.DeleteBookmark(ID);
end;
function TUnitInfo.EditorInfoCount: Integer;
begin
Result := FEditorInfoList.Count;
end;
function TUnitInfo.OpenEditorInfoCount: Integer;
begin
Result := FEditorInfoList.OpenCount;
end;
function TUnitInfo.GetClosedOrNewEditorInfo: TUnitEditorInfo;
begin
if FEditorInfoList.ClosedCount > 0 then
Result := FEditorInfoList.ClosedEditorInfos[0]
else
Result := FEditorInfoList.NewEditorInfo;
end;
procedure TUnitInfo.SetLastUsedEditor(AEditor: TSourceEditorInterface);
begin
FEditorInfoList.SetLastUsedEditor(AEditor);
end;
function TUnitInfo.ReadOnly: boolean;
begin
Result:=UserReadOnly or FileReadOnly;
end;
procedure TUnitInfo.SetSource(ABuffer: TCodeBuffer);
begin
if fSource=ABuffer then begin
if fSource<>nil then
SetTimeStamps;
exit;
end;
if (fSource<>nil) and IsAutoRevertLocked then
fSource.UnlockAutoDiskRevert;
fSource:=ABuffer;
if (fSource<>nil) then begin
SetTimeStamps;
if IsAutoRevertLocked then
fSource.LockAutoDiskRevert;
SetInternalFilename(fSource.FileName);
if (fProject<>nil) and (fProject.MainUnitInfo=Self) then
fProject.MainSourceFilenameChanged;
end;
end;
procedure TUnitInfo.SetSourceLFM(const AValue: TCodeBuffer);
begin
if FSourceLFM=AValue then Exit;
FSourceLFM:=AValue;
end;
procedure TUnitInfo.SetUserReadOnly(const NewValue: boolean);
begin
if NewValue then
Include(FFlags, uifUserReadOnly)
else
Exclude(FFlags, uifUserReadOnly);
if fSource<>nil then
fSource.ReadOnly:=ReadOnly;
end;
function TUnitInfo.GetHasResources:boolean;
begin
Result:=(uifHasResources in FFlags) or (ComponentName<>'');
end;
function TUnitInfo.GetInternalFile: boolean;
begin
Result:=uifInternalFile in FFlags;
end;
function TUnitInfo.GetLoaded: Boolean;
begin
Result:=uifLoaded in FFlags;
end;
function TUnitInfo.GetLoadedDesigner: Boolean;
begin
Result:=uifLoadedDesigner in FFlags;
end;
function TUnitInfo.GetLoadingComponent: boolean;
begin
Result:=uifLoadingComponent in FFlags;
end;
function TUnitInfo.GetEditorInfo(Index: Integer): TUnitEditorInfo;
begin
Result:=FEditorInfoList[Index];
end;
function TUnitInfo.GetFileReadOnly: Boolean;
begin
Result:=uifFileReadOnly in FFlags;
end;
function TUnitInfo.GetHasErrorInLFM: boolean;
begin
Result:=uifHasErrorInLFM in FFlags;
end;
function TUnitInfo.GetModified: boolean;
begin
Result:=(uifModified in FFlags)
or ((Source<>nil) and (Source.ChangeStep<>fSourceChangeStep));
end;
function TUnitInfo.GetNextAutoRevertLockedUnit: TUnitInfo;
begin
Result:=fNext[uilAutoRevertLocked];
end;
function TUnitInfo.GetNextLoadedUnit: TUnitInfo;
begin
Result:=fNext[uilLoaded];
end;
function TUnitInfo.GetNextPartOfProject: TUnitInfo;
begin
Result:=fNext[uilPartOfProject];
end;
function TUnitInfo.GetNextUnitWithComponent: TUnitInfo;
begin
Result:=fNext[uilWithComponent];
end;
function TUnitInfo.GetNextUnitWithEditorIndex: TUnitInfo;
begin
Result:=fNext[uilWithEditorIndex];
end;
function TUnitInfo.GetOpenEditorInfo(Index: Integer): TUnitEditorInfo;
begin
Result := FEditorInfoList.OpenEditorInfos[Index];
end;
function TUnitInfo.GetPrevAutoRevertLockedUnit: TUnitInfo;
begin
Result:=fPrev[uilAutoRevertLocked];
end;
function TUnitInfo.GetPrevLoadedUnit: TUnitInfo;
begin
Result:=fPrev[uilLoaded];
end;
function TUnitInfo.GetPrevPartOfProject: TUnitInfo;
begin
Result:=fPrev[uilPartOfProject];
end;
function TUnitInfo.GetPrevUnitWithComponent: TUnitInfo;
begin
Result:=fPrev[uilWithComponent];
end;
function TUnitInfo.GetPrevUnitWithEditorIndex: TUnitInfo;
begin
Result:=fPrev[uilWithEditorIndex];
end;
function TUnitInfo.GetRunFileIfActive: boolean;
begin
Result:=uifRunFileIfActive in FFlags;
end;
function TUnitInfo.GetSessionModified: boolean;
begin
Result:=uifSessionModified in FFlags;
end;
function TUnitInfo.GetUnitResourceFileformat: TUnitResourcefileFormatClass;
var
ResourceFormats : TUnitResourcefileFormatArr;
i: integer;
begin
if not assigned(FUnitResourceFileformat) then
begin
if Source=nil then
Source:=CodeToolBoss.LoadFile(Filename,true,false);
if Source<>nil then
begin
ResourceFormats := GetUnitResourcefileFormats;
for i := 0 to high(ResourceFormats) do
begin
if ResourceFormats[i].FindResourceDirective(Source) then
begin
FUnitResourceFileformat:=ResourceFormats[i];
Result := FUnitResourceFileformat;
Exit;
end;
end;
end;
FUnitResourceFileformat := LFMUnitResourcefileFormat;
end;
Result := FUnitResourceFileformat;
end;
function TUnitInfo.GetUserReadOnly: Boolean;
begin
Result:=uifUserReadOnly in FFlags;
end;
procedure TUnitInfo.SetAutoReferenceSourceDir(const AValue: boolean);
begin
if AutoReferenceSourceDir=AValue then exit;
if AValue then
Include(FFlags, uifAutoReferenceSourceDir)
else
Exclude(FFlags, uifAutoReferenceSourceDir);
UpdateSourceDirectoryReference;
end;
procedure TUnitInfo.SetBuildFileIfActive(const AValue: boolean);
begin
if BuildFileIfActive=AValue then exit;
if AValue then
Include(FFlags, uifBuildFileIfActive)
else
Exclude(FFlags, uifBuildFileIfActive);
SessionModified:=true;
end;
procedure TUnitInfo.SetCustomDefaultHighlighter(AValue: boolean);
begin
if AValue then
Include(FFlags, uifCustomDefaultHighlighter)
else
Exclude(FFlags, uifCustomDefaultHighlighter);
end;
procedure TUnitInfo.SetDefaultSyntaxHighlighter(
const AValue: TIdeSyntaxHighlighterID);
var
i: Integer;
begin
if FDefaultSyntaxHighlighter = AValue then exit;
FDefaultSyntaxHighlighter := AValue;
for i := 0 to FEditorInfoList.Count - 1 do
if not FEditorInfoList[i].CustomHighlighter then
FEditorInfoList[i].SyntaxHighlighter := AValue;
end;
procedure TUnitInfo.SetDisableI18NForLFM(const AValue: boolean);
begin
if DisableI18NForLFM=AValue then exit;
if AValue then
Include(FFlags, uifDisableI18NForLFM)
else
Exclude(FFlags, uifDisableI18NForLFM);
Modified:=true;
end;
procedure TUnitInfo.SetFileReadOnly(const AValue: Boolean);
begin
if FileReadOnly=AValue then exit;
if AValue then
Include(FFlags, uifFileReadOnly)
else
Exclude(FFlags, uifFileReadOnly);
if fSource<>nil then
fSource.ReadOnly:=ReadOnly;
end;
procedure TUnitInfo.SetComponent(const AValue: TComponent);
begin
if fComponent=AValue then exit;
fComponent:=AValue;
UpdateList(uilWithComponent,fComponent<>nil);
if fComponent=nil then
ClearComponentDependencies
else
FResourceBaseClass:=GetComponentBaseClass(fComponent.ClassType);
end;
procedure TUnitInfo.SetHasErrorInLFM(AValue: boolean);
begin
if HasErrorInLFM=AValue then Exit;
if AValue then
Include(FFlags, uifHasErrorInLFM)
else
Exclude(FFlags, uifHasErrorInLFM);
Modified:=true;
end;
procedure TUnitInfo.SetHasResources(AValue: boolean);
begin
if AValue then
Include(FFlags, uifHasResources)
else
Exclude(FFlags, uifHasResources);
end;
procedure TUnitInfo.SetInternalFile(AValue: boolean);
begin
if AValue then
Include(FFlags, uifInternalFile)
else
Exclude(FFlags, uifInternalFile);
end;
procedure TUnitInfo.SetIsPartOfProject(const AValue: boolean);
begin
if IsPartOfProject=AValue then exit;
if Project<>nil then Project.BeginUpdate(true);
inherited SetIsPartOfProject(AValue);
Modified:=true;
UpdateList(uilPartOfProject,IsPartOfProject);
if IsPartOfProject then UpdateUsageCount(uuIsPartOfProject,0);
UpdateSourceDirectoryReference;
if Project<>nil then Project.EndUpdate;
end;
{-------------------------------------------------------------------------------
procedure TUnitInfo.SetLoaded(const AValue: Boolean);
Loaded is a flag, that is set, when a unit has finished loading into the
editor. It is saved to the project session file and a loaded unit will be
reloaded, when the project is opened.
-------------------------------------------------------------------------------}
procedure TUnitInfo.SetLoaded(const AValue: Boolean);
begin
if Loaded=AValue then exit;
if AValue then begin
Include(FFlags, uifLoaded);
IncreaseAutoRevertLock;
UpdateUsageCount(uuIsLoaded,0);
end else begin
Exclude(FFlags, uifLoaded);
DecreaseAutoRevertLock;
end;
end;
{-------------------------------------------------------------------------------
procedure TUnitInfo.SetLoadedDesigner(const AValue: Boolean);
LoadedDesigner is a flag, that is set, when a visible designer form has
finished opening. It is saved to the project session file and a designer
is restored, when the project is opened and the IDE form editor option
auto open designer forms is enabled.
-------------------------------------------------------------------------------}
procedure TUnitInfo.SetLoadedDesigner(const AValue: Boolean);
begin
if AValue then
Include(FFlags, uifLoadedDesigner)
else
Exclude(FFlags, uifLoadedDesigner);
end;
procedure TUnitInfo.SetLoadingComponent(AValue: boolean);
begin
if AValue then
Include(FFlags, uifLoadingComponent)
else
Exclude(FFlags, uifLoadingComponent);
end;
procedure TUnitInfo.SetModified(const AValue: boolean);
begin
if Modified=AValue then exit;
{$IFDEF VerboseIDEModified}
debugln(['TUnitInfo.SetModified ',Filename,' new Modified=',AValue]);
{$ENDIF}
if AValue then
Include(FFlags, uifModified)
else
Exclude(FFlags, uifModified);
if (not AValue) and Assigned(Source) then
SetTimeStamps;
end;
procedure TUnitInfo.SetProject(const AValue: TProject);
var
ListType: TUnitInfoList;
i: Integer;
begin
if FProject=AValue then exit;
if FProject<>nil then begin
for ListType:=Low(TUnitInfoList) to High(TUnitInfoList) do
Project.RemoveFromList(Self,ListType);
for i := 0 to FEditorInfoList.Count - 1 do
FProject.EditorInfoRemove(FEditorInfoList[i]);
end;
FProject:=AValue;
if FProject<>nil then begin
UpdatePageIndex;
if Component<>nil then
Project.AddToList(Self,uilWithComponent);
if Loaded then
Project.AddToList(Self,uilLoaded);
if IsAutoRevertLocked then
Project.AddToList(Self,uilAutoRevertLocked);
if IsPartOfProject then
Project.AddToList(Self,uilPartOfProject);
for i := 0 to FEditorInfoList.Count - 1 do
FProject.EditorInfoAdd(FEditorInfoList[i]);
end;
UpdateSourceDirectoryReference;
end;
procedure TUnitInfo.SetRunFileIfActive(const AValue: boolean);
begin
if RunFileIfActive=AValue then exit;
if AValue then
Include(FFlags, uifRunFileIfActive)
else
Exclude(FFlags, uifRunFileIfActive);
SessionModified:=true;
end;
procedure TUnitInfo.SetSessionModified(const AValue: boolean);
begin
if SessionModified=AValue then exit;
{$IFDEF VerboseIDEModified}
debugln(['TUnitInfo.SetSessionModified ',Filename,' new Modified=',AValue]);
{$ENDIF}
if AValue then
Include(FFlags, uifSessionModified)
else
Exclude(FFlags, uifSessionModified);
end;
{ TProjectIDEOptions }
constructor TProjectIDEOptions.Create(AProject: TProject);
begin
inherited Create;
FProject := AProject;
end;
destructor TProjectIDEOptions.Destroy;
begin
inherited Destroy;
end;
function TProjectIDEOptions.GetProject: TLazProject;
begin
Result := FProject;
end;
function TProjectIDEOptions.CheckLclApp: Boolean;
begin
FLclApp := FProject.IsLclApplication;
Result := FLclApp;
end;
class function TProjectIDEOptions.GetInstance: TAbstractIDEOptions;
begin
if Project1<>nil then
Result := Project1.IDEOptions
else
Result := nil;
end;
class function TProjectIDEOptions.GetGroupCaption: string;
begin
Result := dlgProjectOptions;
end;
{------------------------------------------------------------------------------
TProject Class
------------------------------------------------------------------------------}
{------------------------------------------------------------------------------
TProject Constructor
------------------------------------------------------------------------------}
constructor TProject.Create(ProjectDescription: TProjectDescriptor);
begin
inherited Create(ProjectDescription);
FActiveWindowIndexAtStart := 0;
FSkipCheckLCLInterfaces:=false;
FAutoCreateForms := true;
FAllEditorsInfoList := TUnitEditorInfoList.Create(nil);
FAllEditorsInfoMap := TMap.Create(ituPtrSize, SizeOf(TObject));
FBookmarks := TProjectBookmarkList.Create;
FMacroEngine:=TTransferMacroList.Create;
FMacroEngine.OnSubstitution:=@MacroEngineSubstitution;
FBuildModes:=TProjectBuildModes.Create(nil);
FBuildModes.LazProject:=Self;
FBuildModesBackup:=TProjectBuildModes.Create(nil);
FBuildModesBackup.LazProject:=Self;
ActiveBuildMode:=FBuildModes.Add('Default');
FDefineTemplates:=TProjectDefineTemplates.Create(Self);
FFlags:=DefaultProjectFlags;
FJumpHistory:=TProjectJumpHistory.Create;
FJumpHistory.OnCheckPosition:=@JumpHistoryCheckPosition;
FJumpHistory.OnLoadSaveFilename:=@LoadSaveFilenameHandler;
fMainUnitID := -1;
fProjectInfoFile := '';
ProjectSessionFile:='';
FSourceDirectories:=TFileReferenceList.Create;
FSourceDirectories.OnChanged:=@SourceDirectoriesChanged;
UpdateProjectDirectory;
FIDEOptions:=TProjectIDEOptions.Create(Self);
FPublishOptions:=TPublishProjectOptions.Create(Self);
FRunParameters:=TRunParamsOptions.Create;
Title := '';
FUnitList := TFPList.Create; // list of TUnitInfo
FOtherDefines := TStringList.Create;
FEnableI18N := False;
FEnableI18NForLFM := True;
FI18NExcludedIdentifiers := TStringList.Create;
FI18NExcludedOriginals := TStringList.Create;
FResources := TProjectResources.Create(Self);
ProjResources.OnModified := @EmbeddedObjectModified;
FHistoryLists := THistoryLists.Create;
FLastCompilerParams := TStringListUTF8Fast.Create;
end;
{------------------------------------------------------------------------------
TProject Destructor
------------------------------------------------------------------------------}
destructor TProject.Destroy;
begin
FDestroying := True;
FDefineTemplates.Active := False;
ActiveBuildMode:=nil;
Clear;
FreeThenNil(FIDEOptions);
FreeAndNil(FBuildModesBackup);
FreeAndNil(FBuildModes);
FreeAndNil(FMacroEngine);
FreeAndNil(FAllEditorsInfoMap);
FreeAndNil(FAllEditorsInfoList);
FreeThenNil(FResources);
FreeThenNil(FBookmarks);
FreeThenNil(FI18NExcludedOriginals);
FreeThenNil(FI18NExcludedIdentifiers);
FreeThenNil(FOtherDefines);
FreeThenNil(FUnitList);
FreeThenNil(FJumpHistory);
FreeThenNil(FSourceDirectories);
FreeThenNil(FPublishOptions);
FreeThenNil(FRunParameters);
FreeThenNil(FDefineTemplates);
FreeAndNil(FHistoryLists);
FreeAndNil(FLastCompilerParams);
inherited Destroy;
end;
{------------------------------------------------------------------------------
Methods for ReadProject
------------------------------------------------------------------------------}
function TProject.LoadOldProjectType(const Path: string): TOldProjectType;
function OldProjectTypeNameToType(const s: string): TOldProjectType;
begin
for Result:=Low(TOldProjectType) to High(TOldProjectType) do
if (CompareText(OldProjectTypeNames[Result],s)=0) then exit;
Result:=ptApplication;
end;
begin
if FFileVersion<=4 then
Result:=OldProjectTypeNameToType(FXMLConfig.GetValue(Path+'General/ProjectType/Value', ''))
else
Result:=ptCustomProgram;
end;
procedure TProject.LoadFlags(const Path: string);
procedure SetFlag(f: TProjectFlag; Value: boolean);
begin
if Value then Include(FFlags,f) else Exclude(FFlags,f);
end;
var
f: TProjectFlag;
OldProjectType: TOldProjectType;
DefFlags: TProjectFlags;
begin
OldProjectType:=LoadOldProjectType(Path);
DefFlags:=DefaultProjectFlags;
if FFileVersion<7 then
Exclude(DefFlags,pfLRSFilesInOutputDirectory);
Flags:=[];
for f:=Low(TProjectFlag) to High(TProjectFlag) do
SetFlag(f,FXMLConfig.GetValue(Path+'General/Flags/'+ProjectFlagNames[f]+'/Value',f in DefFlags));
if FFileVersion<=3 then begin
// set new flags
SetFlag(pfMainUnitIsPascalSource, OldProjectType in [ptProgram,ptApplication]);
SetFlag(pfMainUnitHasUsesSectionForAllUnits, OldProjectType in [ptProgram,ptApplication]);
SetFlag(pfMainUnitHasCreateFormStatements, OldProjectType in [ptApplication]);
SetFlag(pfMainUnitHasTitleStatement,OldProjectType in [ptApplication]);
SetFlag(pfMainUnitHasScaledStatement,OldProjectType in [ptApplication]);
SetFlag(pfRunnable, OldProjectType in [ptProgram,ptApplication,ptCustomProgram]);
end;
if FFileVersion<=11 then begin
// set CompatibilityMode flag for legacy projects (this flag was added in FFileVersion=12 that changed
// item format so that LPI cannot be opened in legacy Lazarus unless pfCompatibilityMode is set)
SetFlag(pfCompatibilityMode, True);
end;
Flags:=Flags-[pfUseDefaultCompilerOptions];
end;
procedure TProject.LoadOtherDefines(const Path: string);
var
Cnt, i: Integer;
SubPath, s: String;
begin
SubPath := 'OtherDefines/';
if not FXMLConfig.HasPath(Path+SubPath, False) then
SubPath := 'CustomDefines/'; // Load from the old path name.
Cnt := FXMLConfig.GetValue(Path+SubPath+'Count', 0);
for i := 0 to Cnt-1 do
begin
s := FXMLConfig.GetValue(Path+SubPath+'Define'+IntToStr(i)+'/Value', '');
if s <> '' then
FOtherDefines.Add(s);
end;
end;
procedure TProject.LoadCustomData(Sender: TObject; Data: TStringToStringTree;
XMLConfig: TXMLConfig; const Path: string);
begin
LoadStringToStringTree(XMLConfig,Data,Path);
if Assigned(OnLoadSafeCustomData) and (Data.Count>0) then
OnLoadSafeCustomData(Sender,true,Data,fPathDelimChanged);
end;
procedure TProject.LoadSessionInfo(const Path: string; Merge: boolean);
// Note: the session can be stored in the lpi as well
// So this method is used for loading the lpi units as well
var
NewUnitInfo: TUnitInfo;
NewUnitCount, i: integer;
SubPath: String;
NewUnitFilename: String;
OldUnitInfo: TUnitInfo;
MergeUnitInfo, LegacyList: Boolean;
begin
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject D reading units');{$ENDIF}
LegacyList:=(FFileVersion<=11) or FXMLConfig.IsLegacyList(Path+'Units/');
NewUnitCount:=FXMLConfig.GetListItemCount(Path+'Units/', 'Unit', LegacyList);
for i := 0 to NewUnitCount - 1 do begin
SubPath:=Path+'Units/'+FXMLConfig.GetListItemXPath('Unit', i, LegacyList)+'/';
NewUnitFilename:=FXMLConfig.GetValue(SubPath+'Filename/Value','');
LoadSaveFilenameHandler(NewUnitFilename,true);
// load unit and add it
OldUnitInfo:=UnitInfoWithFilename(NewUnitFilename);
if OldUnitInfo<>nil then begin
// unit already exists
if Merge then begin
NewUnitInfo:=OldUnitInfo;
MergeUnitInfo:=true;
end else begin
// Doppelganger -> inconsistency found, ignore this file
debugln('TProject.ReadProject file exists twice in lpi file: ignoring "'+NewUnitFilename+'"');
continue;
end;
end else begin
NewUnitInfo:=TUnitInfo.Create(nil);
AddFile(NewUnitInfo,false);
MergeUnitInfo:=false;
end;
NewUnitInfo.LoadFromXMLConfig(FXMLConfig,SubPath,MergeUnitInfo,Merge,FFileVersion);
if i=FNewMainUnitID then begin
MainUnitID:=IndexOf(NewUnitInfo);
FNewMainUnitID:=-1;
end;
end;
// load editor info
i := FXMLConfig.GetValue(Path+'General/ActiveEditorIndexAtStart/Value', -1);
if (i >= 0) then
UpdateVisibleEditor(i); // Load old Config => No WindowIndex
ActiveWindowIndexAtStart := FXMLConfig.GetValue(Path+'General/ActiveWindowIndexAtStart/Value', 0);
FSkipCheckLCLInterfaces:=FXMLConfig.GetValue(Path+'SkipCheckLCLInterfaces/Value',false);
FJumpHistory.LoadFromXMLConfig(FXMLConfig,Path+'');
CleanOutputFileMask:=FXMLConfig.GetValue(Path+'Build/CleanOutputFileMask/Value',
DefaultProjectCleanOutputFileMask);
CleanSourcesFileMask:=FXMLConfig.GetValue(Path+'Build/CleanSourcesFileMask/Value',
DefaultProjectCleanSourcesFileMask);
// load custom session data
LoadStringToStringTree(FXMLConfig,CustomSessionData,Path+'CustomSessionData/');
end;
procedure TProject.LoadFromLPI;
const
Path = ProjOptionsPath;
begin
if (FFileVersion=0) and (FXMLConfig.GetListItemCount(Path+'Units/', 'Unit', true)=0) then
if IDEMessageDialog(lisStrangeLpiFile,
Format(lisTheFileDoesNotLookLikeALpiFile, [ProjectInfoFile]),
mtConfirmation,[mbIgnore,mbAbort])<>mrIgnore
then exit;
LoadFlags(Path);
SessionStorage:=StrToProjectSessionStorage(
FXMLConfig.GetValue(Path+'General/SessionStorage/Value',
ProjectSessionStorageNames[DefaultProjectSessionStorage]));
//DebugLn('TProject.ReadProject SessionStorage=',dbgs(ord(SessionStorage)),' ProjectSessionFile=',ProjectSessionFile);
// load properties
// Note: in FFileVersion<9 the default value was -1
// Since almost all projects have a MainUnit the value 0 was always
// added to the lpi.
// Changing the default value to 0 avoids the redundancy and
// automatically fixes broken lpi files.
FNewMainUnitID := FXMLConfig.GetValue(Path+'General/MainUnit/Value', 0);
Title := FXMLConfig.GetValue(Path+'General/Title/Value', '');
Scaled := FXMLConfig.GetValue(Path+'General/Scaled/Value', False);
AutoCreateForms := FXMLConfig.GetValue(Path+'General/AutoCreateForms/Value', true);
// fpdoc
FPDocPaths:=SwitchPathDelims(FXMLConfig.GetValue(Path+'LazDoc/Paths',''),fPathDelimChanged);
FPDocPackageName:=FXMLConfig.GetValue(Path+'LazDoc/PackageName','');
// i18n
if FFileVersion<6 then begin
POOutputDirectory := SwitchPathDelims(
FXMLConfig.GetValue(Path+'RST/OutDir', ''),fPathDelimChanged);
EnableI18N := POOutputDirectory <> '';
end else begin
EnableI18N := FXMLConfig.GetValue(Path+'i18n/EnableI18N/Value', False);
EnableI18NForLFM := FXMLConfig.GetValue(Path+'i18n/EnableI18N/LFM', True);
POOutputDirectory := SwitchPathDelims(
FXMLConfig.GetValue(Path+'i18n/OutDir/Value', ''),fPathDelimChanged);
LoadStringList(FXMLConfig, FI18NExcludedIdentifiers, Path+'i18n/ExcludedIdentifiers/');
LoadStringList(FXMLConfig, FI18NExcludedOriginals, Path+'i18n/ExcludedOriginals/');
end;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject E reading comp sets');{$ENDIF}
// load custom data
LoadCustomData(Self,CustomData,FXMLConfig,Path+'CustomData/');
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject update ct boss');{$ENDIF}
CodeToolBoss.GlobalValues.Variables[ExternalMacroStart+'ProjPath']:=Directory;
CodeToolBoss.DefineTree.ClearCache;
// load the dependencies
LoadPkgDependencyList(FXMLConfig,Path+'RequiredPackages/',
FFirstRequiredDependency,pddRequires,Self,true,false);
// load the Run and Build parameter Options
RunParameterOptions.Clear;
if FFileVersion<11 then
RunParameterOptions.LegacyLoad(FXMLConfig,Path,fPathDelimChanged)
else
RunParameterOptions.Load(FXMLConfig,Path+'RunParams/',fPathDelimChanged,rpsLPI);
// load the Publish Options
PublishOptions.LoadFromXMLConfig(FXMLConfig,Path+'PublishOptions/',fPathDelimChanged);
// load defines used for custom options
LoadOtherDefines(Path);
// load session info
LoadSessionInfo(Path,false);
if Assigned(FDebuggerLink) then
FDebuggerLink.LoadFromLPI(FXMLConfig, Path);
// call hooks to read their info (e.g. DebugBoss)
if Assigned(OnLoadProjectInfo) then
OnLoadProjectInfo(Self, FXMLConfig, false);
end;
procedure TProject.LoadFromSession;
const
Path = 'ProjectSession/';
var
pds: TPathDelimSwitch;
begin
pds:=CheckPathDelim(FXMLConfig.GetValue(Path+'PathDelim/Value', '/'),
fPathDelimChanged);
SessionStorePathDelim:=pds;
fCurStorePathDelim:=pds;
FFileVersion:=FXMLConfig.GetValue(Path+'Version/Value',0);
// load MacroValues and compiler options
BuildModes.LoadSessionFromXMLConfig(FXMLConfig, Path, FLoadAllOptions);
// load defines used for custom options
LoadOtherDefines(Path);
// load session info
LoadSessionInfo(Path,true);
if FFileVersion>=11 then
RunParameterOptions.Load(FXMLConfig,Path+'RunParams/',fPathDelimChanged,rpsLPS);
HistoryLists.Clear;
if FFileVersion>=12 then
HistoryLists.LoadFromXMLConfig(FXMLConfig,Path+'HistoryLists/');
if Assigned(FDebuggerLink) then
FDebuggerLink.LoadFromSession(FXMLConfig, Path);
// call hooks to read their info (e.g. DebugBoss)
if Assigned(OnLoadProjectInfo) then
OnLoadProjectInfo(Self,FXMLConfig,true);
end;
function TProject.DoLoadLPI(Filename: String): TModalResult;
var
PIFile: String;
begin
Result:=mrOk;
if FLoadAllOptions then
begin
// read the whole lpi, clear any old values
Clear;
ProjectInfoFile:=Filename;
PIFile:=ProjectInfoFile; // May be different from Filename, setter changed.
fProjectInfoFileBuffer:=CodeToolBoss.LoadFile(PIFile,true,true);
fProjectInfoFileBufChangeStamp:=LUInvalidChangeStamp;
try
fProjectInfoFileDate:=FileAgeCached(PIFile);
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject A reading lpi');{$ENDIF}
if fProjectInfoFileBuffer=nil then
FXMLConfig := TCodeBufXMLConfig.CreateWithCache(PIFile,false)
else begin
FXMLConfig := TCodeBufXMLConfig.CreateWithCache(PIFile,false,true,
fProjectInfoFileBuffer.Source);
fProjectInfoFileBufChangeStamp:=fProjectInfoFileBuffer.ChangeStep;
end;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject B done lpi');{$ENDIF}
except
on E: Exception do begin
IDEMessageDialog(lisUnableToReadLpi,
Format(lisUnableToReadTheProjectInfoFile,[LineEnding,PIFile])+LineEnding+E.Message,
mtError, [mbOk]);
Result:=mrCancel;
exit;
end;
end;
fLastReadLPIFilename:=PIFile;
fLastReadLPIFileDate:=Now;
FNewMainUnitID:=-1;
end
else begin
// read only parts of the lpi, keep other values
try
FXMLConfig := TCodeBufXMLConfig.CreateWithCache(Filename,true)
except
on E: Exception do begin
IDEMessageDialog(lisUnableToReadLpi,
Format(lisUnableToReadTheProjectInfoFile,[LineEnding,Filename])+LineEnding+E.Message,
mtError, [mbOk]);
Result:=mrCancel;
exit;
end;
end;
end;
try
// get format
fStorePathDelim:=CheckPathDelim(FXMLConfig.GetValue(ProjOptionsPath+'PathDelim/Value','/'),
fPathDelimChanged);
fCurStorePathDelim:=StorePathDelim;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject C reading values');{$ENDIF}
FFileVersion:= FXMLConfig.GetValue(ProjOptionsPath+'Version/Value',0);
UseAppBundle := FXMLConfig.GetValue(ProjOptionsPath+'General/UseAppBundle/Value', True);
NSPrincipalClass := FXMLConfig.GetValue(ProjOptionsPath+'General/NSPrincipalClass/Value', '');
if FLoadAllOptions then
LoadFromLPI;
// Resources
ProjResources.ReadFromProjectFile(FXMLConfig, ProjOptionsPath, FLoadAllOptions);
// load MacroValues and compiler options
ClearBuildModes;
BuildModes.LoadProjOptsFromXMLConfig(FXMLConfig, ProjOptionsPath);
// load matrix options
BuildModes.SharedMatrixOptions.LoadFromXMLConfig(FXMLConfig,
ProjOptionsPath+'BuildModes/SharedMatrixOptions/');
finally
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject freeing xml');{$ENDIF}
fPathDelimChanged:=false;
try
FXMLConfig.Modified:=false;
FXMLConfig.Free;
except
end;
FXMLConfig:=nil;
end;
end;
function TProject.DoLoadSession(Filename: String): TModalResult;
begin
Result:=mrOK;
if FileExistsUTF8(Filename) then
begin
//DebugLn('TProject.ReadProject loading Session Filename=',Filename);
try
FXMLConfig := TCodeBufXMLConfig.CreateWithCache(Filename);
LoadFromSession;
except
IDEMessageDialog(lisCCOErrorCaption,
Format(lisUnableToReadTheProjectInfoFile, [LineEnding,Filename]),
mtError,[mbOk]);
Result:=mrCancel;
exit;
end;
fPathDelimChanged:=false;
try
FXMLConfig.Modified:=false;
FXMLConfig.Free;
except
end;
fCurStorePathDelim:=StorePathDelim;
FXMLConfig:=nil;
end else
// there is no .lps file -> create some defaults
LoadDefaultSession;
end;
function TProject.DoLoadLPR(Revert: boolean): TModalResult;
// lpr is here the main module, it does not need to have the extension .lpr
var
LPRUnitInfo: TUnitInfo;
begin
Result:=mrOk;
if (MainUnitID<0) or (not (pfMainUnitIsPascalSource in Flags)) then
exit; // has no lpr
LPRUnitInfo:=MainUnitInfo;
if (LPRUnitInfo.Source=nil) then begin
LPRUnitInfo.Source:=CodeToolBoss.LoadFile(LPRUnitInfo.Filename,true,Revert);
if LPRUnitInfo.Source=nil then exit(mrCancel);
end;
UpdateIsPartOfProjectFromMainUnit;
end;
// Method ReadProject itself
function TProject.ReadProject(const NewProjectInfoFile: string;
GlobalMatrixOptions: TBuildMatrixOptions; LoadAllOptions: Boolean): TModalResult;
begin
Result := mrCancel;
BeginUpdate(true);
try
if Assigned(FDebuggerLink) then
FDebuggerLink.BeforeReadProject;
BuildModes.FGlobalMatrixOptions := GlobalMatrixOptions;
FLoadAllOptions := LoadAllOptions;
// load project lpi file
Result:=DoLoadLPI(NewProjectInfoFile);
if Result<>mrOK then Exit;
// load session file (if available)
if (SessionStorage in pssHasSeparateSession)
and (CompareFilenames(ProjectInfoFile,ProjectSessionFile)<>0)
and FLoadAllOptions then
begin
Result:=DoLoadSession(ProjectSessionFile);
if Result<>mrOK then Exit;
end;
// load lpr
if (pfMainUnitIsPascalSource in Flags) and (MainUnitInfo<>nil) then
DoLoadLPR(false); // ignore errors
finally
EndUpdate;
FAllEditorsInfoList.SortByPageIndex;
end;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject END');{$ENDIF}
if Assigned(FDebuggerLink) then
FDebuggerLink.AfterReadProject;
Result := mrOk;
end;
{------------------------------------------------------------------------------
Methods for TProject WriteProject
------------------------------------------------------------------------------}
procedure TProject.SaveFlags(const Path: string);
var
f: TProjectFlag;
begin
for f:=Low(TProjectFlag) to High(TProjectFlag) do begin
FXMLConfig.SetDeleteValue(Path+'General/Flags/'
+ProjectFlagNames[f]+'/Value', f in Flags, f in DefaultProjectFlags);
end;
end;
procedure TProject.SaveUnits(const Path: string; SaveSession: boolean);
var
i, SaveUnitCount: integer;
begin
SaveUnitCount:=0;
for i:=0 to UnitCount-1 do
if UnitMustBeSaved(Units[i],FProjectWriteFlags,SaveSession) then begin
Units[i].SaveToXMLConfig(FXMLConfig,
Path+'Units/'+FXMLConfig.GetListItemXPath('Unit', SaveUnitCount, UseLegacyLists)+'/',True,SaveSession,fCurStorePathDelim);
inc(SaveUnitCount);
end;
FXMLConfig.SetListItemCount(Path+'Units/',SaveUnitCount,UseLegacyLists);
end;
procedure TProject.SaveOtherDefines(const Path: string);
var
i: integer;
begin
for i:=0 to FOtherDefines.Count-1 do
FXMLConfig.SetDeleteValue(Path+'OtherDefines/Define'+IntToStr(i)+'/Value',
FOtherDefines[i],'');
FXMLConfig.SetDeleteValue(Path+'OtherDefines/Count',FOtherDefines.Count,0);
end;
procedure TProject.SaveCustomData(Sender: TObject; Data: TStringToStringTree;
XMLConfig: TXMLConfig; const Path: string);
var
NewData: TStringToStringTree;
begin
if Assigned(OnLoadSafeCustomData) and (Data.Count>0) then
begin
NewData:=TStringToStringTree.Create(Data.CompareItemsFunc,Data.CompareKeyItemFunc,Data.CaseSensitive);
try
NewData.Assign(Data);
OnLoadSafeCustomData(Sender,false,NewData,fPathDelimChanged);
SaveStringToStringTree(XMLConfig,NewData,Path);
finally
NewData.Free;
end;
end else begin
SaveStringToStringTree(XMLConfig,Data,Path);
end;
end;
procedure TProject.SaveSessionInfo(const Path: string);
begin
FXMLConfig.DeleteValue(Path+'General/ActiveEditorIndexAtStart/Value');
FXMLConfig.SetDeleteValue(Path+'General/ActiveWindowIndexAtStart/Value',
ActiveWindowIndexAtStart,0);
FXMLConfig.SetDeleteValue('SkipCheckLCLInterfaces/Value',
FSkipCheckLCLInterfaces,false);
FXMLConfig.SetDeleteValue(Path+'Build/CleanOutputFileMask/Value',
CleanOutputFileMask,DefaultProjectCleanOutputFileMask);
FXMLConfig.SetDeleteValue(Path+'Build/CleanSourcesFileMask/Value',
CleanSourcesFileMask,DefaultProjectCleanSourcesFileMask);
if (not (pfSaveOnlyProjectUnits in Flags))
and (not (pwfSkipJumpPoints in FProjectWriteFlags)) then begin
if (pfSaveJumpHistory in Flags) then begin
FJumpHistory.DeleteInvalidPositions;
FJumpHistory.SaveToXMLConfig(FXMLConfig,Path,UseLegacyLists);
end
else
FXMLConfig.DeletePath(Path+'JumpHistory');
end;
// save custom session data
SaveStringToStringTree(FXMLConfig,CustomSessionData,Path+'CustomSessionData/');
end;
procedure TProject.SaveToLPI;
const
Path = ProjOptionsPath;
var
CurFlags: TProjectWriteFlags;
begin
FFileVersion:=ProjectInfoFileVersion;
// format
FXMLConfig.SetValue(Path+'Version/Value',ProjectInfoFileVersion);
FXMLConfig.SetDeleteValue(Path+'PathDelim/Value',PathDelimSwitchToDelim[fCurStorePathDelim],'/');
SaveFlags(Path);
FXMLConfig.SetDeleteValue(Path+'General/SessionStorage/Value',
ProjectSessionStorageNames[SessionStorage],
ProjectSessionStorageNames[DefaultProjectSessionStorage]);
// general properties
FXMLConfig.SetDeleteValue(Path+'General/MainUnit/Value', MainUnitID ,0);
FXMLConfig.SetDeleteValue(Path+'General/AutoCreateForms/Value',
AutoCreateForms,true);
FXMLConfig.SetDeleteValue(Path+'General/Title/Value', Title,'');
FXMLConfig.SetDeleteValue(Path+'General/Scaled/Value', Scaled,False);
FXMLConfig.SetDeleteValue(Path+'General/UseAppBundle/Value', UseAppBundle, True);
FXMLConfig.SetDeleteValue(Path+'General/NSPrincipalClass/Value', NSPrincipalClass, '');
// fpdoc
FXMLConfig.SetDeleteValue(Path+'LazDoc/Paths',
SwitchPathDelims(CreateRelativeSearchPath(FPDocPaths,Directory),
fCurStorePathDelim), '');
FXMLConfig.SetDeleteValue(Path+'LazDoc/PackageName',FPDocPackageName,'');
// i18n
FXMLConfig.SetDeleteValue(Path+'i18n/EnableI18N/Value', EnableI18N, false);
FXMLConfig.SetDeleteValue(Path+'i18n/EnableI18N/LFM', EnableI18NForLFM, true);
FXMLConfig.SetDeleteValue(Path+'i18n/OutDir/Value',
SwitchPathDelims(CreateRelativePath(POOutputDirectory,Directory),
fCurStorePathDelim), '');
SaveStringList(FXMLConfig, FI18NExcludedIdentifiers, Path+'i18n/ExcludedIdentifiers/');
SaveStringList(FXMLConfig, FI18NExcludedOriginals, Path+'i18n/ExcludedOriginals/');
// Resources
ProjResources.WriteToProjectFile(FXMLConfig, Path);
// save custom data
SaveCustomData(Self,CustomData,FXMLConfig,Path+'CustomData/');
// Save the macro values and compiler options
BuildModes.SaveProjOptsToXMLConfig(FXMLConfig, Path, FSaveSessionInLPI, UseLegacyLists);
BuildModes.SaveSharedMatrixOptions(Path);
if FSaveSessionInLPI then
BuildModes.SaveSessionData(Path);
// save the Publish Options
PublishOptions.SaveToXMLConfig(FXMLConfig,Path+'PublishOptions/',fCurStorePathDelim);
// save the Run and Build parameter options
if pfCompatibilityMode in Flags then
RunParameterOptions.LegacySave(FXMLConfig,Path,fCurStorePathDelim);
RunParameterOptions.Save(FXMLConfig,Path+'RunParams/',fCurStorePathDelim,rpsLPI, UseLegacyLists);
// save dependencies
SavePkgDependencyList(FXMLConfig,Path+'RequiredPackages/',
FFirstRequiredDependency,pddRequires,fCurStorePathDelim,pfCompatibilityMode in FFlags);
// save units
SaveUnits(Path,FSaveSessionInLPI);
if Assigned(FDebuggerLink) then
FDebuggerLink.SaveToLPI(FXMLConfig, Path);
if FSaveSessionInLPI then begin
// save defines used for custom options
SaveOtherDefines(Path);
// save session info
SaveSessionInfo(Path);
end;
// Notifiy hooks
if Assigned(OnSaveProjectInfo) then begin
CurFlags:=FProjectWriteFlags;
if not FSaveSessionInLPI then
CurFlags:=CurFlags+[pwfSkipSeparateSessionInfo];
if UseLegacyLists then
CurFlags:=CurFlags+[pwfCompatibilityMode];
OnSaveProjectInfo(Self,FXMLConfig,CurFlags);
end;
if FXMLConfig.Modified or (not FileExistsCached(FXMLConfig.Filename)) then
begin
// backup
if Assigned(fOnFileBackup) then begin
if fOnFileBackup(FXMLConfig.Filename)=mrAbort then begin
debugln(['Error: (lazarus) [TProject.SaveToLPI] backup of "'+FXMLConfig.Filename+'" failed.']);
exit;
end;
end;
// save lpi to disk
//debugln(['TProject.WriteProject ',DbgSName(FXMLConfig),' FCfgFilename=',FCfgFilename]);
FXMLConfig.Flush;
end;
if not (pwfIgnoreModified in FProjectWriteFlags) then
Modified:=false;
if FSaveSessionInLPI then
SessionModified:=false;
end;
procedure TProject.SaveToSession;
const
Path = 'ProjectSession/';
begin
FFileVersion:=ProjectInfoFileVersion;
fCurStorePathDelim:=SessionStorePathDelim;
FXMLConfig.SetDeleteValue(Path+'PathDelim/Value',
PathDelimSwitchToDelim[fCurStorePathDelim],'/');
FXMLConfig.SetValue(Path+'Version/Value',ProjectInfoFileVersion);
// Save the session build modes
BuildModes.SaveSessionOptsToXMLConfig(FXMLConfig, Path, True, UseLegacyLists);
BuildModes.SaveSessionData(Path);
// save all units
SaveUnits(Path,true);
if Assigned(FDebuggerLink) then
FDebuggerLink.SaveToSession(FXMLConfig, Path);
// save defines used for custom options
SaveOtherDefines(Path);
// save session info
SaveSessionInfo(Path);
// save the Run and Build parameter options
RunParameterOptions.Save(FXMLConfig,Path+'RunParams/',fCurStorePathDelim,rpsLPS, UseLegacyLists);
// save history lists
HistoryLists.SaveToXMLConfig(FXMLConfig,Path+'HistoryLists/', UseLegacyLists);
// Notifiy hooks
if Assigned(OnSaveProjectInfo) then
OnSaveProjectInfo(Self,FXMLConfig,FProjectWriteFlags+[pwfSkipProjectInfo]);
end;
function TProject.DoWrite(Filename: String; IsLpi: Boolean): TModalResult;
var
Msg: String;
begin
repeat
Result := mrOK;
try
FXMLConfig := TCodeBufXMLConfig.CreateWithCache(Filename,false);
except
on E: Exception do begin
DebugLn('ERROR: ',E.Message);
if IsLpi then
Msg:=lisUnableToWriteTheProjectInfoFileError
else
Msg:=lisUnableToWriteTheProjectSessionFileError;
IDEMessageDialog(lisCodeToolsDefsWriteError,
Format(Msg, [LineEnding, Filename, LineEnding, E.Message])
,mtError,[mbOk]);
Result:=mrCancel;
exit;
end;
end;
try
// Now actually write the data either to LPI file or to session file.
if IsLpi then
SaveToLPI
else
SaveToSession;
except
on E: Exception do begin
Result:=IDEMessageDialog(lisCodeToolsDefsWriteError,
Format(lisUnableToWriteToFile2, [Filename]), mtError,[mbRetry,mbAbort]);
end;
end;
if IsLpi and (CompareFilenames(ProjectInfoFile,FXMLConfig.Filename)=0) then
UpdateFileBuffer;
try
FXMLConfig.Free;
except
end;
FXMLConfig:=nil;
until Result<>mrRetry;
end;
function TProject.GetDirectory: string;
begin
Result:=fProjectDirectory;
end;
// Method WriteProject itself
function TProject.WriteProject(ProjectWriteFlags: TProjectWriteFlags;
const OverrideProjectInfoFile: string;
GlobalMatrixOptions: TBuildMatrixOptions): TModalResult;
var
CfgFilename: String;
SessFilename: String; // only set if session should be saved to a separate file
SessionResult: TModalResult;
WriteLPI, WriteLPS: Boolean;
begin
Result := mrCancel;
fCurStorePathDelim:=StorePathDelim;
if OverrideProjectInfoFile<>'' then
CfgFilename := OverrideProjectInfoFile
else
CfgFilename := ProjectInfoFile;
CfgFilename:=SetDirSeparators(CfgFilename);
SessFilename := '';
if (not (pwfSkipSeparateSessionInfo in ProjectWriteFlags))
and (SessionStorage in pssHasSeparateSession) then begin
// save session in separate file .lps
if OverrideProjectInfoFile<>'' then
SessFilename := ChangeFileExt(OverrideProjectInfoFile,'.lps')
else
SessFilename := ProjectSessionFile;
if (CompareFilenames(SessFilename,CfgFilename)=0) then
SessFilename:='';
end;
//DebugLn('TProject.WriteProject Write Session File="',SessFilename,'"');
DoDirSeparators(SessFilename);
FProjectWriteFlags := ProjectWriteFlags;
BuildModes.FGlobalMatrixOptions := GlobalMatrixOptions;
// first save the .lpi file
if (pwfSkipSeparateSessionInfo in ProjectWriteFlags) or (SessionStorage=pssNone) then
FSaveSessionInLPI:=false
else
FSaveSessionInLPI:=(SessFilename='') or (CompareFilenames(SessFilename,CfgFilename)=0);
// check if modified
if pwfIgnoreModified in ProjectWriteFlags then
begin
WriteLPI:=true;
WriteLPS:=true;
end
else begin
WriteLPI:=SomeDataModified or (not FileExistsUTF8(CfgFilename));
if (CompareFilenames(ProjectInfoFile,CfgFilename)=0) then
// save to default lpi
WriteLPI:=WriteLPI or (fProjectInfoFileDate<>FileAgeCached(CfgFilename))
else
// save to another file
WriteLPI:=true;
if SessFilename='' then begin
WriteLPS:=false;
WriteLPI:=WriteLPI or SomeSessionModified;
end else begin
WriteLPS:=WriteLPI or SomeSessionModified or (not FileExistsUTF8(SessFilename));
end;
//debugln(['TProject.WriteProject WriteLPI=',WriteLPI,' WriteLPS=',WriteLPS]);
if not (WriteLPI or WriteLPS) then exit(mrOk);
end;
//debugln(['TProject.WriteProject WriteLPI=',WriteLPI,' WriteLPS=',WriteLPS,' Modified=',Modified,' SessionModified=',SessionModified]);
// increase usage counters
UpdateUsageCounts(CfgFilename);
if WriteLPI then
// Write to LPI
Result:=DoWrite(CfgFilename, True);
if (SessFilename<>'') and WriteLPS then begin
// save session in separate file .lps
if Assigned(fOnFileBackup) then begin
Result:=fOnFileBackup(SessFilename);
if Result=mrAbort then exit;
end;
SessionResult:=DoWrite(SessFilename, False);
if (Result=mrOk) and (SessionResult<>mrOk) then
Result:=SessionResult;
end;
end;
procedure TProject.UpdateExecutableType;
function GetMainSourceType: string;
var
AnUnitInfo: TUnitInfo;
begin
Result:='';
if MainUnitID<0 then exit;
AnUnitInfo:=Units[MainUnitID];
if AnUnitInfo.Source=nil then exit;
Result:=CodeToolBoss.GetSourceType(AnUnitInfo.Source,false);
end;
var
SourceType: String;
begin
SourceType:=GetMainSourceType;
if SysUtils.CompareText(SourceType,'Program')=0 then
ExecutableType:=petProgram
else if SysUtils.CompareText(SourceType,'Library')=0 then
ExecutableType:=petLibrary
else if SysUtils.CompareText(SourceType,'Unit')=0 then
ExecutableType:=petUnit
else if SysUtils.CompareText(SourceType,'Package')=0 then
ExecutableType:=petPackage
else
ExecutableType:=ProjectIntf.petNone;
end;
procedure TProject.BackupSession;
begin
FSessionModifiedBackup:=SessionModified;
end;
procedure TProject.RestoreSession;
begin
SessionModified:=FSessionModifiedBackup;
end;
procedure TProject.BackupBuildModes;
begin
FActiveBuildModeBackup:=BuildModes.IndexOf(ActiveBuildMode);
{$IFDEF VerboseIDEModified}
debugln(['TProject.BackupBuildModes START=====================']);
{$ENDIF}
FBuildModesBackup.Assign(BuildModes,true);
{$IFDEF VerboseIDEModified}
debugln(['TProject.BackupBuildModes END===================== Modified=',Modified]);
{$ENDIF}
end;
procedure TProject.RestoreBuildModes;
begin
Assert(FBuildModesBackup.Count>0, 'TProject.RestoreBuildModes: FBuildModesBackup.Count=0');
ActiveBuildMode:=nil;
BuildModes.Assign(FBuildModesBackup,true);
if (FActiveBuildModeBackup>=0) and (FActiveBuildModeBackup<BuildModes.Count)
then
ActiveBuildMode:=BuildModes[FActiveBuildModeBackup]
else
ActiveBuildMode:=BuildModes[0];
end;
function TProject.GetTitle: string;
begin
Result:=Title;
if not MacroEngine.SubstituteStr(Result) then
debugln(['TProject.GetTitle failed Title="',Title,'"']);
end;
function TProject.TitleIsDefault(Fuzzy: boolean): boolean;
var
t: String;
p: Integer;
begin
Result:=true;
t:=Title;
if (t='') or (t=GetDefaultTitle) then exit;
if Fuzzy and (SysUtils.CompareText(t,GetDefaultTitle)=0) then exit;
// check for project+number
p:=length(t);
while (p>0) and (t[p] in ['0'..'9']) do dec(p);
if SysUtils.CompareText(copy(t,1,p),'project')=0 then exit;
Result:=false;
end;
function TProject.GetIDAsString: string;
begin
Result:='Project'; // TODO: see TLazPackage, when this is changed change also TProjectDefineTemplates.UpdateSrcDirIfDef
end;
function TProject.GetIDAsWord: string;
begin
Result:='Project'; // TODO: see TLazPackage when this is changed change also TProjectDefineTemplates.UpdateSrcDirIfDef
end;
{------------------------------------------------------------------------------
TProject AddFile
------------------------------------------------------------------------------}
procedure TProject.AddFile(ProjectFile: TLazProjectFile; AddToProjectUsesClause: boolean);
var
NewIndex: integer;
AnUnit: TUnitInfo;
s: String;
begin
AnUnit:=ProjectFile as TUnitInfo;
//debugln('TProject.AddFile A ',AnUnit.Filename,' AddToProjectFile=',dbgs(AddToProjectFile));
if (UnitInfoWithFilename(AnUnit.Filename)<>nil) and (AnUnit.FileName <> '') then
debugln(['TProject.AddFile WARNING: file already in unit list: ',AnUnit.Filename]);
BeginUpdate(true);
NewIndex:=UnitCount;
FUnitList.Add(AnUnit);
AnUnit.Project:=Self;
AnUnit.OnFileBackup:=@FileBackupHandler;
AnUnit.OnLoadSaveFilename:=@LoadSaveFilenameHandler;
AnUnit.OnUnitNameChange:=@UnitNameChangeHandler;
// lock the main unit (when it is changed on disk it must *not* auto revert)
if MainUnitID=NewIndex then
MainUnitInfo.IncreaseAutoRevertLock;
if AddToProjectUsesClause and (MainUnitID>=0) and (MainUnitID<>NewIndex) then
begin
s:=AnUnit.GetUsesUnitName;
if s<>'' then // add unit to uses section
CodeToolBoss.AddUnitToMainUsesSectionIfNeeded(MainUnitInfo.Source,s,'',[aufLast]);
end;
EndUpdate;
UnitModified(AnUnit);
end;
{------------------------------------------------------------------------------
TProject RemoveUnit
------------------------------------------------------------------------------}
procedure TProject.RemoveUnit(Index: integer; RemoveFromUsesSection: boolean);
var
OldUnitInfo: TUnitInfo;
begin
if (Index<0) or (Index>=UnitCount) then begin
raise Exception.Create('ERROR: TProject.RemoveUnit index out of bounds');
end;
if (Index=MainUnitID) then begin
raise Exception.Create('ERROR: TProject.RemoveUnit index = MainUnit');
end;
BeginUpdate(true);
OldUnitInfo:=Units[Index];
UnitModified(OldUnitInfo);
if (MainUnitID>=0) then begin
// remove unit from uses section and from createforms in program file
if (OldUnitInfo.IsPartOfProject) then begin
if RemoveFromUsesSection then begin
if (OldUnitInfo.Unit_Name<>'') then begin
CodeToolBoss.RemoveUnitFromAllUsesSections(MainUnitInfo.Source,
OldUnitInfo.Unit_Name);
end;
if (OldUnitInfo.ComponentName<>'') then begin
CodeToolBoss.RemoveCreateFormStatement(MainUnitInfo.Source,
OldUnitInfo.ComponentName);
end;
end;
end;
end;
// delete bookmarks of this unit
Bookmarks.DeleteAllWithUnitInfo(OldUnitInfo);
// adjust MainUnit
if MainUnitID>=Index then dec(fMainUnitID);
// delete unitinfo instance
OldUnitInfo.Free;
FUnitList.Delete(Index);
EndUpdate;
end;
function TProject.CreateProjectFile(const Filename: string): TLazProjectFile;
var
NewBuf: TCodeBuffer;
AnUnitInfo: TUnitInfo;
begin
NewBuf:=CodeToolBoss.CreateFile(Filename);
AnUnitInfo:=TUnitInfo.Create(NewBuf);
if IDEEditorOptions<>nil then
AnUnitInfo.DefaultSyntaxHighlighter := FilenameToLazSyntaxHighlighter(NewBuf.Filename);
Result:=AnUnitInfo;
end;
function TProject.GetAndUpdateVisibleUnit(AnEditor: TSourceEditorInterface;
AWindowID: Integer): TUnitInfo;
var
i: Integer;
AnEditorInfo: TUnitEditorInfo;
begin
for i := 0 to AllEditorsInfoCount - 1 do
with AllEditorsInfo[i] do
if AllEditorsInfo[i].WindowID = AWindowID then
IsVisibleTab := (EditorComponent = AnEditor);
AnEditorInfo := EditorInfoWithEditorComponent(AnEditor);
if AnEditorInfo = nil then Exit(nil);
Result := AnEditorInfo.UnitInfo;
if Assigned(Result) then
Result.SetLastUsedEditor(AnEditor);
end;
procedure TProject.UpdateAllVisibleUnits;
var
i, j: Integer;
aWndId: LongInt;
Info: TUnitEditorInfo;
begin
for i := 0 to AllEditorsInfoCount - 1 do begin
Info:=AllEditorsInfo[i];
aWndId:=Info.WindowID;
j := SourceEditorManagerIntf.IndexOfSourceWindowWithID(aWndId);
Info.IsVisibleTab := (aWndId>=0) and (j >= 0)
and (Info.EditorComponent = SourceEditorManagerIntf.SourceWindows[j].ActiveEditor);
end;
end;
function TProject.RemoveNonExistingFiles(RemoveFromUsesSection: boolean): boolean;
var
i: Integer;
AnUnitInfo: TUnitInfo;
begin
Result:=false;
i:=UnitCount-1;
while (i>=0) do begin
if i<UnitCount then begin
AnUnitInfo:=Units[i];
if (not AnUnitInfo.IsVirtual) and (i<>MainUnitID) then begin
if not FileExistsUTF8(AnUnitInfo.Filename) then begin
RemoveUnit(i,RemoveFromUsesSection);
Result:=true;
end;
end;
end;
dec(i);
end;
end;
{------------------------------------------------------------------------------
TProject Clear
------------------------------------------------------------------------------}
procedure TProject.Clear;
var i:integer;
begin
BeginUpdate(true);
inherited Clear;
// break and free removed dependencies
while FFirstRemovedDependency<>nil do
DeleteRemovedDependency(FFirstRemovedDependency);
// break and free required dependencies
while FFirstRequiredDependency<>nil do
DeleteRequiredDependency(FFirstRequiredDependency);
// delete files
for i:=0 to UnitCount-1 do Units[i].Free;
FUnitList.Clear;
RunParameters.Clear;
FActiveWindowIndexAtStart := -1;
FSkipCheckLCLInterfaces:=false;
FAutoOpenDesignerFormsDisabled := false;
FEnableI18N:=false;
FEnableI18NForLFM:=true;
FI18NExcludedOriginals.Clear;
FI18NExcludedIdentifiers.Clear;
FBookmarks.Clear;
ClearBuildModes;
FDefineTemplates.Clear;
FJumpHistory.Clear;
fMainUnitID := -1;
fProjectInfoFile := '';
ProjectSessionFile:='';
FStateFileDate:=0;
FStateFlags:=[];
ClearSourceDirectories;
UpdateProjectDirectory;
FPublishOptions.Clear;
Title := '';
if Assigned(FDebuggerLink) then
FDebuggerLink.Clear;
Modified := false;
SessionModified := false;
EndUpdate;
end;
procedure TProject.BeginUpdate(Change: boolean);
begin
inc(FUpdateLock);
FDefineTemplates.BeginUpdate;
FSourceDirectories.BeginUpdate;
if FUpdateLock=1 then begin
fChanged:=Change;
if Assigned(OnBeginUpdate) then OnBeginUpdate(Self);
end else
fChanged:=fChanged or Change;
end;
procedure TProject.EndUpdate;
begin
if FUpdateLock<=0 then RaiseGDBException('TProject.EndUpdate');
dec(FUpdateLock);
FSourceDirectories.EndUpdate;
FDefineTemplates.EndUpdate;
if FUpdateLock=0 then begin
if Assigned(OnEndUpdate) then OnEndUpdate(Self,fChanged);
end;
end;
procedure TProject.UnitModified(AnUnitInfo: TUnitInfo);
begin
if AnUnitInfo.IsPartOfProject then begin
{$IFDEF VerboseIDEModified}
debugln(['TProject.UnitModified ',AnUnitInfo.Filename]);
{$ENDIF}
Modified:=true;
end else
SessionModified:=true;
end;
function TProject.NeedsDefineTemplates: boolean;
begin
Result:=not Destroying;
end;
procedure TProject.BeginRevertUnit(AnUnitInfo: TUnitInfo);
begin
if AnUnitInfo<>nil then
inc(AnUnitInfo.FRevertLockCount);
inc(FRevertLockCount);
if FRevertLockCount=1 then begin
Include(FStateFlags,lpsfPropertyDependenciesChanged);
ClearUnitComponentDependencies([ucdtOldProperty,ucdtProperty]);
LockUnitComponentDependencies;
UpdateUnitComponentDependencies;
end;
end;
procedure TProject.EndRevertUnit(AnUnitInfo: TUnitInfo);
begin
if FRevertLockCount<=0 then
raise Exception.Create('TProject.EndRevertUnit Project');
if (AnUnitInfo<>nil) and (AnUnitInfo.FRevertLockCount<=0) then
raise Exception.Create('TProject.EndRevertUnit Filename='+AnUnitInfo.Filename);
if AnUnitInfo<>nil then
dec(AnUnitInfo.FRevertLockCount);
dec(FRevertLockCount);
if FRevertLockCount=0 then
UnlockUnitComponentDependencies;
end;
function TProject.IsReverting(AnUnitInfo: TUnitInfo): boolean;
begin
if AnUnitInfo=nil then
Result:=FRevertLockCount>0
else
Result:=AnUnitInfo.FRevertLockCount>0;
end;
function TProject.GetUnits(Index:integer):TUnitInfo;
begin
Result:=TUnitInfo(FUnitList[Index]);
end;
procedure TProject.SetFlags(const AValue: TProjectFlags);
begin
inherited SetFlags(AValue);
end;
procedure TProject.SetMainUnitID(const AValue: Integer);
begin
if AValue>=UnitCount then
RaiseGDBException('');
if MainUnitID=AValue then exit;
if (MainUnitID>=0) and (MainUnitID<UnitCount) then
MainUnitInfo.DecreaseAutoRevertLock;
fMainUnitID:=AValue;
if (MainUnitID>=0) and (MainUnitID<UnitCount) then
MainUnitInfo.IncreaseAutoRevertLock;
end;
function TProject.GetFiles(Index: integer): TLazProjectFile;
begin
Result:=Units[Index];
end;
function TProject.GetModified: boolean;
begin
Result:=(FChangeStamp<>FChangeStampSaved)
or ((BuildModes<>nil) and BuildModes.Modified);
end;
procedure TProject.SetModified(const AValue: boolean);
begin
{$IFDEF VerboseIDEModified}
if Modified<>AValue then begin
debugln(['TProject.SetModified ================= ',AValue,' ',FChangeStamp]);
CTDumpStack;
end;
{$ENDIF}
if fDestroying then exit;
if AValue then
IncreaseChangeStamp
else begin
FChangeStampSaved:=FChangeStamp;
PublishOptions.Modified := False;
ProjResources.Modified := False;
BuildModes.Modified:=false;
SessionModified := False;
end;
end;
procedure TProject.SetSessionModified(const AValue: boolean);
begin
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetSessionModified new Modified=',AValue]);
{$ENDIF}
inherited SetSessionModified(AValue);
if AValue then
IncreaseSessionChangeStamp;
end;
procedure TProject.SetExecutableType(const AValue: TProjectExecutableType);
begin
inherited SetExecutableType(AValue);
case ExecutableType of
petLibrary: CompilerOptions.ExecutableType:=cetLibrary;
else CompilerOptions.ExecutableType:=cetProgram;
end;
end;
function TProject.GetUseManifest: boolean;
begin
Result:=ProjResources.XPManifest.UseManifest;
end;
procedure TProject.SetUseManifest(AValue: boolean);
begin
ProjResources.XPManifest.UseManifest:=AValue;
end;
function TProject.UnitCount:integer;
begin
Result:=FUnitList.Count;
end;
function TProject.GetFileCount: integer;
begin
Result:=UnitCount;
end;
function ExpUnitName(const AnUnitName: string): string;
begin
Result:=UpperCase(ExtractFileNameOnly(AnUnitName));
end;
function TProject.UnitNameExists(const AnUnitName: string): boolean;
var
i: integer;
ExpName: string;
begin
Result:=true;
ExpName:=ExpUnitName(AnUnitName);
if ExpUnitName(fProjectInfoFile)=ExpName then exit;
for i:=0 to UnitCount-1 do
if Units[i].IsPartOfProject and (ExpUnitName(Units[i].FileName)=ExpName) then
exit;
Result:=false;
end;
function TProject.NewUniqueUnitName(const AnUnitName: string): string;
var
u: integer;
Prefix: string;
begin
Prefix:=AnUnitName;
while (Prefix<>'') and (Prefix[length(Prefix)] in ['0'..'9']) do
SetLength(Prefix,length(Prefix)-1);
if not IsValidIdent(Prefix) then
Prefix:='Unit';
u:=0;
repeat
inc(u);
Result:=Prefix+IntToStr(u);
until (not UnitNameExists(Result));
end;
function TProject.NewUniqueFilename(const Filename: string): string;
var
FileNOnly, FileExt: String;
i: Integer;
begin
FileNOnly:=ExtractFilenameOnly(Filename);
while (FileNOnly<>'') and (FileNOnly[length(FileNOnly)] in ['0'..'9']) do
SetLength(FileNOnly,length(FileNOnly)-1);
FileExt:=ExtractFileExt(Filename);
i:=0;
repeat
inc(i);
Result:=FileNOnly+IntToStr(i)+FileExt;
until ProjectUnitWithShortFilename(Result)=nil;
end;
function TProject.AddCreateFormToProjectFile(const AClassName, AName: string): boolean;
begin
Result:=CodeToolBoss.AddCreateFormStatement(MainUnitInfo.Source,AClassName,AName);
if Result then
MainUnitInfo.Modified:=true;
end;
function TProject.RemoveCreateFormFromProjectFile(const AName:string):boolean;
begin
Result:=CodeToolBoss.RemoveCreateFormStatement(MainUnitInfo.Source,AName);
if Result then
MainUnitInfo.Modified:=true;
end;
function TProject.FormIsCreatedInProjectFile(const AClassname,AName:string): boolean;
var p: integer;
begin
Result:=(CodeToolBoss.FindCreateFormStatement(MainUnitInfo.Source,
1,AClassName,AName,p)=0);
if p=0 then ;
end;
function TProject.IndexOfUnitWithName(const AnUnitName:string;
OnlyProjectUnits:boolean; IgnoreUnit: TUnitInfo):integer;
begin
if AnUnitName='' then exit(-1);
Result:=UnitCount-1;
while (Result>=0) do begin
if ((OnlyProjectUnits and Units[Result].IsPartOfProject)
or (not OnlyProjectUnits))
and (IgnoreUnit<>Units[Result])
and (Units[Result].Unit_Name<>'')
then begin
if (CompareDottedIdentifiers(PChar(Units[Result].Unit_Name),PChar(AnUnitName))=0)
then
exit;
end;
dec(Result);
end;
end;
function TProject.IndexOfUnitWithComponent(AComponent: TComponent;
OnlyProjectUnits:boolean; IgnoreUnit: TUnitInfo):integer;
begin
Result:=UnitCount-1;
while (Result>=0) do begin
if (OnlyProjectUnits and Units[Result].IsPartOfProject)
or (not OnlyProjectUnits)
and (IgnoreUnit<>Units[Result]) then begin
if Units[Result].Component=AComponent then
exit;
end;
dec(Result);
end;
end;
function TProject.IndexOfUnitWithComponentName(const AComponentName: string;
OnlyProjectUnits: boolean; IgnoreUnit: TUnitInfo): integer;
begin
Result:=UnitCount-1;
while (Result>=0) do begin
if ((OnlyProjectUnits and Units[Result].IsPartOfProject)
or (not OnlyProjectUnits))
and (IgnoreUnit<>Units[Result]) then begin
if (CompareText(Units[Result].ComponentName,AComponentName)=0)
or ((Units[Result].Component<>nil)
and (CompareText(Units[Result].Component.Name,AComponentName)=0))
then
exit;
end;
dec(Result);
end;
end;
function TProject.UnitWithEditorComponent(AEditor: TSourceEditorInterface): TUnitInfo;
var
AnEditorInfo: TUnitEditorInfo;
begin
if AEditor = nil then exit(nil);
AnEditorInfo := EditorInfoWithEditorComponent(AEditor);
if AnEditorInfo = nil then exit(nil);
Result := AnEditorInfo.UnitInfo;
end;
function TProject.GetResourceFile(AnUnitInfo: TUnitInfo; Index:integer): TCodeBuffer;
var i, LinkIndex: integer;
begin
LinkIndex:=-1;
i:=0;
Result:=nil;
while (i<Index) do begin
inc(i);
Result:=CodeToolBoss.FindNextResourceFile(AnUnitInfo.Source,LinkIndex);
end;
end;
procedure TProject.LoadDefaultIcon;
begin
TProjectIcon(ProjResources[TProjectIcon]).LoadDefaultIcon;
end;
function TProject.GetShortFilename(const Filename: string; UseUp: boolean): string;
var
BaseDir: String;
CurPath: String;
begin
Result:=Filename;
BaseDir:=AppendPathDelim(Directory);
if (BaseDir<>'') and FilenameIsAbsolute(BaseDir) and UseUp then
Result:=CreateRelativePath(Result,BaseDir)
else begin
CurPath:=copy(ExtractFilePath(Result),1,length(BaseDir));
if CompareFilenames(BaseDir,CurPath)=0 then
delete(Result,1,length(CurPath));
end;
end;
procedure TProject.ConvertToLPIFilename(var AFilename: string);
begin
LoadSaveFilenameHandler(AFilename,false);
end;
procedure TProject.ConvertFromLPIFilename(var AFilename: string);
begin
LoadSaveFilenameHandler(AFilename,true);
end;
function TProject.GetMainResourceFilename(AnUnitInfo: TUnitInfo):string;
var CodeBuf: TCodeBuffer;
begin
CodeBuf:=GetResourceFile(AnUnitInfo,1);
if CodeBuf=nil then begin
if AnUnitInfo.Filename='' then exit('');
Result:=ChangeFileExt(AnUnitInfo.Filename,ResourceFileExt);
exit;
end else
Result:=CodeBuf.Filename;
end;
function TProject.IsLclApplication: Boolean;
var
CodeTool: TCodeTool;
UsesNode: TCodeTreeNode;
begin
Result := False;
// LCL dependency must be there.
if FindDependencyByName('LCL') = Nil then Exit;
//DebugLn(['IsLclApplication: Found LCL dependency.']);
try
// Check is uses section has "Forms" unit.
if not CodeToolBoss.InitCurCodeTool(MainUnitInfo.Source) then Exit;
CodeTool := CodeToolBoss.CurCodeTool;
CodeTool.BuildTree(lsrMainUsesSectionEnd);
UsesNode := CodeTool.FindMainUsesNode;
if UsesNode = Nil then Exit;
//DebugLn(['IsLclApplication: Found "uses" node.']);
if CodeTool.FindNameInUsesSection(UsesNode, 'forms') = Nil then Exit;
//DebugLn(['IsLclApplication: Found "Forms" unit.']);
Result := True;
except
DebugLn(['IsLclApplication: Codetools could not parse the source.']);
end;
end;
function TProject.IsVirtual: boolean;
begin
Result:=((MainUnitID>=0) and MainUnitInfo.IsVirtual)
or (ProjectInfoFile='') or (not FilenameIsAbsolute(ProjectInfoFile));
end;
function TProject.IndexOf(AUnitInfo: TUnitInfo):integer;
begin
Result:=UnitCount-1;
while (Result>=0) and (Units[Result]<>AUnitInfo) do dec(Result);
end;
procedure TProject.AddToOrRemoveFromComponentList(AnUnitInfo: TUnitInfo);
begin
if AnUnitInfo.Component=nil then begin
RemoveFromList(AnUnitInfo,uilWithComponent);
end else begin
AddToList(AnUnitInfo,uilWithComponent);
end;
end;
procedure TProject.AddToOrRemoveFromLoadedList(AnUnitInfo: TUnitInfo);
begin
if not AnUnitInfo.Loaded then begin
RemoveFromList(AnUnitInfo,uilLoaded);
end else begin
AddToList(AnUnitInfo,uilLoaded);
end;
end;
procedure TProject.AddToOrRemoveFromAutoRevertLockedList(AnUnitInfo: TUnitInfo);
begin
if not AnUnitInfo.IsAutoRevertLocked then begin
RemoveFromList(AnUnitInfo,uilAutoRevertLocked);
end else begin
AddToList(AnUnitInfo,uilAutoRevertLocked);
end;
end;
procedure TProject.AddToOrRemoveFromPartOfProjectList(AnUnitInfo: TUnitInfo);
begin
if not AnUnitInfo.IsPartOfProject then begin
RemoveFromList(AnUnitInfo,uilPartOfProject);
end else begin
AddToList(AnUnitInfo,uilPartOfProject);
end;
end;
function TProject.GetTargetFilename: string;
begin
Result:=FLazCompilerOptions.TargetFilename;
end;
procedure TProject.SetTargetFilename(const NewTargetFilename: string);
begin
FLazCompilerOptions.TargetFilename:=NewTargetFilename;
end;
procedure TProject.SetEnableI18N(const AValue: boolean);
begin
if FEnableI18N=AValue then exit;
FEnableI18N:=AValue;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetEnableI18N ',AValue]);
{$ENDIF}
Modified:=true;
end;
procedure TProject.SetPOOutputDirectory(const AValue: string);
var
NewValue: String;
begin
NewValue:=ChompPathDelim(TrimFilename(AValue));
if FPOOutputDirectory=NewValue then exit;
FPOOutputDirectory:=NewValue;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetPOOutputDirectory ',AValue]);
{$ENDIF}
Modified:=true;
end;
function TProject.GetMainFilename: String;
begin
if MainUnitID>=0 then Result:=MainUnitInfo.Filename
else Result:='';
end;
function TProject.GetFirstPartOfProject: TUnitInfo;
begin
Result:=FFirst[uilPartOfProject];
end;
function TProject.GetFirstLoadedUnit: TUnitInfo;
begin
Result:=fFirst[uilLoaded];
end;
procedure TProject.EmbeddedObjectModified(Sender: TObject);
begin
if ProjResources.Modified then
Modified := True;
end;
function TProject.GetFirstAutoRevertLockedUnit: TUnitInfo;
begin
Result:=fFirst[uilAutoRevertLocked];
end;
function TProject.GetAllEditorsInfo(Index: Integer): TUnitEditorInfo;
begin
Result := FAllEditorsInfoList[Index];
end;
function TProject.GetCompilerOptions: TProjectCompilerOptions;
begin
Result := TProjectCompilerOptions(FLazCompilerOptions);
end;
function TProject.GetBaseCompilerOptions: TBaseCompilerOptions;
// This satisfies the IProjPack interface requirement.
begin
Result := TBaseCompilerOptions(FLazCompilerOptions);
end;
procedure TProject.ClearBuildModes;
begin
ActiveBuildMode:=nil;
FBuildModes.Clear;
if not fDestroying then
ActiveBuildMode:=FBuildModes.Add('default');
end;
function TProject.GetActiveBuildModeID: string;
begin
Result := ActiveBuildMode.Identifier;
end;
function TProject.GetFirstUnitWithComponent: TUnitInfo;
begin
Result:=fFirst[uilWithComponent];
end;
function TProject.GetFirstUnitWithEditorIndex: TUnitInfo;
begin
Result:=fFirst[uilWithEditorIndex];
end;
function TProject.GetIDEOptions: TProjectIDEOptions;
begin
Result := TProjectIDEOptions(FIDEOptions);
end;
function TProject.GetMainUnitInfo: TUnitInfo;
begin
if (MainUnitID>=0) and (MainUnitID<UnitCount) then
Result:=Units[MainUnitID]
else
Result:=nil;
end;
function TProject.GetProjResources: TProjectResources;
begin
Result:=TProjectResources(Resources);
end;
function TProject.GetRunParameterOptions: TRunParamsOptions;
begin
Result:=TRunParamsOptions(FRunParameters);
end;
function TProject.GetSourceDirectories: TFileReferenceList;
begin
Result:=FSourceDirectories;
end;
function TProject.GetProjectInfoFile:string;
begin
Result:=fProjectInfoFile;
end;
procedure TProject.SetProjectInfoFile(const NewFilename:string);
var
NewProjectInfoFile: String;
TitleWasDefault: Boolean;
begin
NewProjectInfoFile:=TrimFilename(NewFilename);
if NewProjectInfoFile='' then exit;
ForcePathDelims(NewProjectInfoFile);
if fProjectInfoFile=NewProjectInfoFile then exit;
BeginUpdate(true);
TitleWasDefault:=(Title<>'') and TitleIsDefault(true);
fProjectInfoFile:=NewProjectInfoFile;
if TitleWasDefault then
Title:=GetDefaultTitle;
UpdateProjectDirectory;
UpdateSessionFilename;
if Assigned(OnChangeProjectInfoFile) then
OnChangeProjectInfoFile(Self);
FDefineTemplates.SourceDirectoriesChanged;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetProjectInfoFile ',NewFilename]);
{$ENDIF}
Modified:=true;
EndUpdate;
//DebugLn('TProject.SetProjectInfoFile FDefineTemplates.FUpdateLock=',dbgs(FDefineTemplates.FUpdateLock));
end;
procedure TProject.SetSessionStorage(const AValue: TProjectSessionStorage);
begin
if SessionStorage=AValue then exit;
inherited SetSessionStorage(AValue);
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetSessionStorage ']);
{$ENDIF}
Modified:=true;
UpdateSessionFilename;
end;
function TProject.FileBackupHandler(const Filename: string): TModalResult;
begin
if Assigned(fOnFileBackup) then
Result:=fOnFileBackup(Filename)
else
Result:=mrOk;
end;
procedure TProject.LoadSaveFilenameHandler(var AFilename: string; Load:boolean);
{ This function is used after reading a filename from the config
and before writing a filename to a config.
The config can be the lpi or the session.
}
var
ProjectPath: string;
FileWasAbsolute: Boolean;
function FileCanBeMadeRelative: boolean;
begin
Result:=false;
if not FileWasAbsolute then exit;
{$IFdef MSWindows}
// check that the file is on the same drive / filesystem
if CompareText(ExtractFileDrive(AFilename),ExtractFileDrive(ProjectPath))<>0
then exit;
{$ENDIF}
Result:=true;
end;
begin
if AFileName='' then exit;
//debugln(['TProject.OnLoadSaveFilename A "',AFilename,'" fPathDelimChanged=',fPathDelimChanged,' Load=',Load]);
if Load and fPathDelimChanged then begin
{$IFDEF MSWindows}
// PathDelim changed from '/' to '\'
FileWasAbsolute:=FilenameIsUnixAbsolute(AFileName);
{$ELSE}
// PathDelim changed from '\' to '/'
FileWasAbsolute:=FilenameIsWinAbsolute(AFileName);
{$ENDIF}
ForcePathDelims(AFilename);
end else begin
FileWasAbsolute:=FilenameIsAbsolute(AFileName);
end;
AFilename:=TrimFilename(AFilename);
ProjectPath:=AppendPathDelim(Directory);
if ProjectPath<>'' then begin
if Load then begin
// make filename absolute
if not FileWasAbsolute then
AFilename:=TrimFilename(ProjectPath+AFilename);
end else begin
// try making filename relative to project file
if FileCanBeMadeRelative then
AFilename:=CreateRelativePath(AFilename,ProjectPath);
end;
end;
if (not Load) then begin
if (not IsCurrentPathDelim(fCurStorePathDelim))
and (FilenameIsAbsolute(AFileName))
and (ProjectPath<>'') then begin
// the lpi file is saved with different pathdelims
// this will destroy absolute paths
// => force it relative
AFileName:=ExtractRelativepath(ProjectPath,AFilename);
end;
AFilename:=SwitchPathDelims(AFileName,fCurStorePathDelim);
end;
//debugln('TProject.OnLoadSaveFilename END "',AFilename,'" FileWasAbsolute=',dbgs(FileWasAbsolute));
end;
function TProject.RemoveProjectPathFromFilename(const AFilename: string): string;
var
ProjectPath:string;
begin
ProjectPath:=Directory;
if ProjectPath='' then ProjectPath:=GetCurrentDirUTF8;
Result:=AFilename;
ForcePathDelims(Result);
// try making filename relative to project file
if FilenameIsAbsolute(Result)
and (CompareFileNames(copy(Result,1,length(ProjectPath)),ProjectPath)=0) then
Result:=copy(Result,length(ProjectPath)+1,
length(Result)-length(ProjectPath));
end;
function TProject.FileIsInProjectDir(const AFilename: string): boolean;
var
ProjectDir, FilePath: string;
begin
if FilenameIsAbsolute(AFilename) then
begin
if IsVirtual then
Result:=false
else begin
ProjectDir:=Directory;
FilePath:=LeftStr(AFilename,length(ProjectDir));
Result:=CompareFileNames(ProjectDir,FilePath)=0;
end;
end else
Result:=true; // Returns True also when AFilename=''
end;
procedure TProject.GetVirtualDefines(DefTree: TDefineTree; DirDef: TDirectoryDefines);
procedure ExtendPath(const AVariable, APath: string);
var
TempValue: string;
begin
if APath<>'' then begin
DefTree.ReadValue(DirDef,APath+';','',TempValue);
DirDef.Values.Prepend(AVariable,TempValue);
end;
end;
begin
if (not IsVirtual) then exit;
ExtendPath(NamespacesMacroName,CompilerOptions.Namespaces);
ExtendPath(UnitPathMacroName,CompilerOptions.OtherUnitFiles);
ExtendPath(IncludePathMacroName,CompilerOptions.IncludePath);
ExtendPath(SrcPathMacroName,CompilerOptions.SrcPath);
end;
procedure TProject.GetSourcesChangedOnDisk(var ACodeBufferList: TFPList);
procedure Add(aCode: TCodeBuffer);
begin
if aCode=nil then exit;
if not aCode.FileOnDiskHasChanged then exit;
if ACodeBufferList=nil then
ACodeBufferList:=TFPList.Create;
if ACodeBufferList.IndexOf(aCode)<0 then
ACodeBufferList.Add(aCode);
end;
var
AnUnitInfo: TUnitInfo;
begin
AnUnitInfo:=fFirst[uilAutoRevertLocked];
while (AnUnitInfo<>nil) do begin
Add(AnUnitInfo.Source);
Add(AnUnitInfo.SourceLFM);
AnUnitInfo:=AnUnitInfo.fNext[uilAutoRevertLocked];
end;
end;
function TProject.GetUseLegacyLists: Boolean;
begin
Result:=pfCompatibilityMode in Flags;
end;
function TProject.HasProjectInfoFileChangedOnDisk: boolean;
var
AnUnitInfo: TUnitInfo;
Code: TCodeBuffer;
begin
Result:=false;
if IsVirtual or Modified then exit;
AnUnitInfo:=UnitInfoWithFilename(ProjectInfoFile,[pfsfOnlyEditorFiles]);
if (AnUnitInfo<>nil) then begin
// user is editing the lpi file in source editor
exit;
end;
AnUnitInfo:=fFirst[uilAutoRevertLocked];
while (AnUnitInfo<>nil) do begin
if CompareFilenames(AnUnitInfo.Filename,ProjectInfoFile)=0 then begin
// revert is locked for this file
exit;
end;
AnUnitInfo:=AnUnitInfo.fNext[uilAutoRevertLocked];
end;
if not FileExistsCached(ProjectInfoFile) then exit;
if fProjectInfoFileDate=FileAgeCached(ProjectInfoFile) then exit;
// file on disk has changed, check content
Code:=CodeToolBoss.LoadFile(ProjectInfoFile,true,true);
if (Code<>nil) and (Code=fProjectInfoFileBuffer)
and (Code.ChangeStep=fProjectInfoFileBufChangeStamp)
then exit;
//DebugLn(['TProject.HasProjectInfoFileChangedOnDisk ',ProjectInfoFile,' fProjectInfoFileDate=',fProjectInfoFileDate,' ',FileAgeUTF8(ProjectInfoFile)]);
Result:=true;
end;
procedure TProject.IgnoreProjectInfoFileOnDisk;
begin
fProjectInfoFileDate:=FileAgeCached(ProjectInfoFile);
end;
function TProject.FindDependencyByName(const PackageName: string): TPkgDependency;
begin
Result:=FindDependencyByNameInList(FFirstRequiredDependency,pddRequires,
PackageName);
end;
function TProject.FindRemovedDependencyByName(const PkgName: string): TPkgDependency;
begin
Result:=FindDependencyByNameInList(FFirstRemovedDependency,pddRequires,PkgName);
end;
function TProject.RequiredDepByIndex(Index: integer): TPkgDependency;
begin
Result:=GetDependencyWithIndex(FFirstRequiredDependency,pddRequires,Index);
end;
function TProject.RemovedDepByIndex(Index: integer): TPkgDependency;
begin
Result:=GetDependencyWithIndex(FFirstRemovedDependency,pddRequires,Index);
end;
procedure TProject.AddRequiredDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.AddToList(FFirstRequiredDependency,pddRequires);
Dependency.Owner:=Self;
Dependency.HoldPackage:=true;
FDefineTemplates.CustomDefinesChanged;
{$IFDEF VerboseAddProjPkg}
DebugLn(['TProject.AddRequiredDependency ']);
{$ENDIF}
IncreaseCompilerParseStamp;
{$IFDEF VerboseIDEModified}
debugln(['TProject.AddRequiredDependency ',Dependency.PackageName]);
{$ENDIF}
Modified:=true;
EndUpdate;
end;
procedure TProject.RemoveRequiredDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RemoveFromList(FFirstRequiredDependency,pddRequires);
Dependency.RequiredPackage:=nil;
Dependency.AddToList(FFirstRemovedDependency,pddRequires);
Dependency.Removed:=true;
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
{$IFDEF VerboseIDEModified}
debugln(['TProject.RemoveRequiredDependency ',Dependency.PackageName]);
{$ENDIF}
Modified:=true;
EndUpdate;
end;
procedure TProject.DeleteRequiredDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RequiredPackage:=nil;
Dependency.RemoveFromList(FFirstRequiredDependency,pddRequires);
Dependency.Free;
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
EndUpdate;
end;
procedure TProject.DeleteRemovedDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RequiredPackage:=nil;
Dependency.RemoveFromList(FFirstRemovedDependency,pddRequires);
Dependency.Free;
EndUpdate;
end;
procedure TProject.RemoveRemovedDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RemoveFromList(FFirstRemovedDependency,pddRequires);
Dependency.Removed:=false;
EndUpdate;
end;
procedure TProject.ReaddRemovedDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
RemoveRemovedDependency(Dependency);
AddRequiredDependency(Dependency);
EndUpdate;
end;
procedure TProject.MoveRequiredDependencyUp(Dependency: TPkgDependency);
begin
if Dependency.PrevRequiresDependency=nil then exit;
BeginUpdate(true);
Dependency.MoveUpInList(FFirstRequiredDependency,pddRequires);
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
EndUpdate;
end;
procedure TProject.MoveRequiredDependencyDown(Dependency: TPkgDependency);
begin
if Dependency.NextRequiresDependency=nil then exit;
BeginUpdate(true);
Dependency.MoveDownInList(FFirstRequiredDependency,pddRequires);
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
EndUpdate;
end;
function TProject.Requires(APackage: TLazPackage; SearchRecursively: boolean): boolean;
begin
if SearchRecursively then
Result:=PackageGraph.FindDependencyRecursively(FFirstRequiredDependency,
APackage)<>nil
else
Result:=FindCompatibleDependencyInList(FFirstRequiredDependency,pddRequires,
APackage)<>nil;
end;
procedure TProject.GetAllRequiredPackages(var List: TFPList;
ReqFlags: TPkgIntfRequiredFlags; MinPolicy: TPackageUpdatePolicy);
var
FPMakeList: TFPList;
begin
if Assigned(OnGetAllRequiredPackages) then begin
OnGetAllRequiredPackages(nil,FirstRequiredDependency,List,FPMakeList,ReqFlags,MinPolicy);
FPMakeList.Free;
end;
end;
procedure TProject.AddPackageDependency(const PackageName: string);
var
PkgDependency: TPkgDependency;
begin
if FindDependencyByNameInList(FirstRequiredDependency,pddRequires,PackageName)
<>nil then exit;
PkgDependency:=TPkgDependency.Create;
PkgDependency.DependencyType:=pdtLazarus;
PkgDependency.PackageName:=PackageName;
AddRequiredDependency(PkgDependency);
end;
function TProject.RemovePackageDependency(const PackageName: string): boolean;
var
PkgDependency: TPkgDependency;
begin
PkgDependency:=FindDependencyByNameInList(FirstRequiredDependency,pddRequires,PackageName);
Result := Assigned(PkgDependency);
if Result then
RemoveRequiredDependency(PkgDependency);
end;
procedure TProject.LockUnitComponentDependencies;
begin
inc(FLockUnitComponentDependencies);
if FLockUnitComponentDependencies=1 then begin
// update once
Include(FStateFlags,lpsfPropertyDependenciesChanged);
Include(FStateFlags,lpsfDesignerChanged);
end;
end;
procedure TProject.UnlockUnitComponentDependencies;
begin
if FLockUnitComponentDependencies=0 then
raise Exception.Create('');
dec(FLockUnitComponentDependencies);
end;
procedure TProject.UpdateUnitComponentDependencies;
procedure Search(AnUnitInfo: TUnitInfo; AComponent: TComponent);
// search the published properties of AComponent for references to other units
var
TypeInfo: PTypeInfo;
TypeData: PTypeData;
PropInfo: PPropInfo;
PropList: PPropList;
CurCount,i: integer;
ReferenceComp: TObject;
OwnerComponent: TComponent;
ReferenceUnit: TUnitInfo;
Dependency: TUnitComponentDependency;
begin
if AComponent<>AnUnitInfo.Component then begin
ReferenceUnit:=UnitWithComponentClass(TComponentClass(AComponent.ClassType));
{$ifdef VerboseFormEditor}
DebugLn(['Search UnitComponent=',DbgSName(AnUnitInfo.Component),' AComponent=',DbgSName(AComponent),' ReferenceUnit=',ReferenceUnit<>nil]);
{$endif}
if (ReferenceUnit<>nil) then begin
// component class references another unit
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies inline component found: ',DbgSName(AComponent),' ',AnUnitInfo.Filename,' -> ',ReferenceUnit.Filename]);
{$ENDIF}
AnUnitInfo.AddRequiresComponentDependency(
ReferenceUnit,[ucdtInlineClass]);
end;
end;
// read all properties and remove doubles
TypeInfo:=PTypeInfo(AComponent.ClassInfo);
repeat
// read all property infos of current class
TypeData:=GetTypeData(TypeInfo);
// read property count
CurCount:=GetPropList(TypeInfo,PropList);
try
// read properties
for i:=0 to CurCount-1 do begin
PropInfo:=PropList^[i];
if (PropInfo^.PropType^.Kind=tkClass) then begin
// property of kind TObject
ReferenceComp:=GetObjectProp(AComponent,PropInfo);
//debugln('TProject.UpdateUnitComponentDependencies Property ',dbgsName(AComponent),' Name=',PropInfo^.Name,' Type=',PropInfo^.PropType^.Name,' Value=',dbgsName(ReferenceComp),' TypeInfo=',TypeInfo^.Name);
if ReferenceComp is TComponent then begin
// reference is a TComponent
OwnerComponent:=TComponent(ReferenceComp);
while OwnerComponent.Owner<>nil do
OwnerComponent:=OwnerComponent.Owner;
if OwnerComponent<>AnUnitInfo.Component then begin
// property references a component that is not owned
// by the current unit
ReferenceUnit:=UnitWithComponent(OwnerComponent);
if ReferenceUnit<>nil then begin
// property references another unit
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies multi form reference found: ',AnUnitInfo.Filename,' -> ',ReferenceUnit.Filename]);
{$ENDIF}
AnUnitInfo.AddRequiresComponentDependency(
ReferenceUnit,[ucdtProperty]);
if FRevertLockCount>0 then begin
Dependency:=AnUnitInfo.AddRequiresComponentDependency(
ReferenceUnit,[ucdtOldProperty]);
Dependency.SetUsedByPropPath(
Dependency.CreatePropPath(AComponent,PropInfo^.Name),
Dependency.CreatePropPath(TComponent(ReferenceComp)));
end;
end;
end;
end;
end;
end;
finally
FreeMem(PropList);
end;
TypeInfo:=TypeData^.ParentInfo;
until TypeInfo=nil;
end;
procedure DFSRequiredDesigner(AnUnitInfo, IgnoreUnitInfo: TUnitInfo);
var
Dependency: TUnitComponentDependency;
UsingUnitInfo: TUnitInfo;
begin
if (AnUnitInfo=nil) or (AnUnitInfo.Component=nil)
or (uifMarked in AnUnitInfo.FFlags) then
exit;
Include(AnUnitInfo.FFlags,uifMarked);
Dependency:=AnUnitInfo.FirstRequiredComponent;
while Dependency<>nil do begin
UsingUnitInfo:=Dependency.RequiresUnit;
if (UsingUnitInfo<>IgnoreUnitInfo)
and (not (uifComponentIndirectlyUsedByDesigner in UsingUnitInfo.FFlags))
then begin
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies.DFSRequiredDesigner designer of ',AnUnitInfo.Filename,' uses ',UsingUnitInfo.Filename]);
{$ENDIF}
Include(UsingUnitInfo.FFlags,uifComponentIndirectlyUsedByDesigner);
DFSRequiredDesigner(UsingUnitInfo,IgnoreUnitInfo);
end;
Dependency:=Dependency.NextRequiresDependency;
end;
end;
var
AnUnitInfo: TUnitInfo;
i: Integer;
begin
if (FLockUnitComponentDependencies=0)
or (lpsfPropertyDependenciesChanged in FStateFlags) then begin
Exclude(FStateFlags,lpsfPropertyDependenciesChanged);
// clear dependencies
ClearUnitComponentDependencies([ucdtProperty,ucdtInlineClass]);
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies checking properties ...']);
{$ENDIF}
// find property dependencies
AnUnitInfo:=FirstUnitWithComponent;
while AnUnitInfo<>nil do begin
Search(AnUnitInfo,AnUnitInfo.Component);
for i:=AnUnitInfo.Component.ComponentCount-1 downto 0 do
Search(AnUnitInfo,AnUnitInfo.Component.Components[i]);
AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
end;
//WriteDebugReportUnitComponentDependencies('P ');
end;
if (FLockUnitComponentDependencies=0)
or (lpsfDesignerChanged in FStateFlags) then begin
Exclude(FStateFlags,lpsfDesignerChanged);
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies checking designers ...']);
{$ENDIF}
// find designer dependencies
AnUnitInfo:=FirstUnitWithComponent;
while AnUnitInfo<>nil do begin
AnUnitInfo.FFlags:=AnUnitInfo.FFlags-
[uifMarked,uifComponentIndirectlyUsedByDesigner,uifComponentUsedByDesigner];
if FindRootDesigner(AnUnitInfo.Component)<>nil then begin
{$IFDEF VerboseIDEMultiForm}
DebugLn(['TProject.UpdateUnitComponentDependencies used by designer: ',AnUnitInfo.Filename]);
{$ENDIF}
Include(AnUnitInfo.FFlags,uifComponentUsedByDesigner);
end;
AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
end;
// mark all units that are used indirectly by a designer
AnUnitInfo:=FirstUnitWithComponent;
while AnUnitInfo<>nil do begin
if (uifComponentUsedByDesigner in AnUnitInfo.FFlags) then
begin
// mark all that use indirectly this designer
DFSRequiredDesigner(AnUnitInfo,AnUnitInfo);
end;
AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
end;
{$IFDEF VerboseTFrame}
WriteDebugReportUnitComponentDependencies('UUCD ');
{$ENDIF}
end;
end;
procedure TProject.InvalidateUnitComponentDesignerDependencies;
begin
Include(FStateFlags,lpsfDesignerChanged);
end;
procedure TProject.ClearUnitComponentDependencies(ClearTypes: TUnitCompDependencyTypes);
var
i: Integer;
begin
for i:=UnitCount-1 downto 0 do
Units[i].ClearUnitComponentDependencies(ClearTypes);
end;
procedure TProject.FindUnitsUsingSubComponent(SubComponent: TComponent;
List: TFPList; IgnoreOwner: boolean);
procedure Search(AnUnitInfo: TUnitInfo; AComponent: TComponent);
// search the published properties of AComponent for references to other units
var
TypeInfo: PTypeInfo;
TypeData: PTypeData;
PropInfo: PPropInfo;
PropList: PPropList;
CurCount,i: integer;
RefObj: TObject;
begin
if csDestroying in AComponent.ComponentState then exit;
// read all properties and remove doubles
TypeInfo:=PTypeInfo(AComponent.ClassInfo);
repeat
// read all property infos of current class
TypeData:=GetTypeData(TypeInfo);
// read property count
CurCount:=GetPropList(TypeInfo,PropList);
try
// read properties
for i:=0 to CurCount-1 do
begin
PropInfo:=PropList^[i];
if PropInfo^.PropType^.Kind=tkClass then
begin
// property of kind TObject
RefObj:=GetObjectProp(AComponent,PropInfo);
//DebugLn('TProject.FindUnitsUsingSubComponent Property ',dbgsName(AComponent),
// ' Name=',PropInfo^.Name,' Type=',PropInfo^.PropType^.Name,
// ' Value=',dbgsName(RefObj),' TypeInfo=',TypeInfo^.Name);
if RefObj=SubComponent then
if List.IndexOf(AnUnitInfo)<0 then
List.Add(AnUnitInfo);
end;
end;
finally
FreeMem(PropList);
end;
TypeInfo:=TypeData^.ParentInfo;
until TypeInfo=nil;
end;
var
AnUnitInfo: TUnitInfo;
i: Integer;
OwnerComponent: TComponent;
begin
if SubComponent=nil then exit;
if IgnoreOwner then begin
OwnerComponent:=SubComponent;
while OwnerComponent<>nil do
OwnerComponent:=OwnerComponent.Owner;
end else
OwnerComponent:=nil;
AnUnitInfo:=FirstUnitWithComponent;
while AnUnitInfo<>nil do begin
if csDestroying in AnUnitInfo.Component.ComponentState then continue;
if AnUnitInfo.Component<>OwnerComponent then begin
Search(AnUnitInfo,AnUnitInfo.Component);
for i:=AnUnitInfo.Component.ComponentCount-1 downto 0 do
Search(AnUnitInfo,AnUnitInfo.Component.Components[i]);
end;
AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
end;
end;
procedure TProject.WriteDebugReportUnitComponentDependencies(Prefix: string);
var
i: Integer;
AnUnitInfo: TUnitInfo;
begin
for i:=0 to UnitCount-1 do begin
AnUnitInfo:=Units[i];
if (AnUnitInfo.FirstUsedByComponent<>nil)
or (AnUnitInfo.FirstRequiredComponent<>nil) then
AnUnitInfo.WriteDebugReportUnitComponentDependencies(Prefix);
end;
end;
procedure TProject.AddSrcPath(const SrcPathAddition: string);
begin
CompilerOptions.MergeToSrcPath( GetForcedPathDelims(SrcPathAddition) );
end;
function TProject.GetSourceDirs(WithProjectDir, WithoutOutputDir: boolean): string;
begin
Result:=SourceDirectories.CreateSearchPathFromAllFiles;
if WithProjectDir then
Result:=MergeSearchPaths(Result,Directory);
if WithoutOutputDir then
Result:=RemoveSearchPaths(Result,GetOutputDirectory);
end;
function TProject.GetOutputDirectory: string;
begin
Result:=CompilerOptions.ParsedOpts.GetParsedValue(pcosOutputDir);
end;
function TProject.GetCompilerFilename: string;
begin
Result:=CompilerOptions.ParsedOpts.GetParsedValue(pcosCompilerPath);
end;
function TProject.GetStateFilename: string;
begin
Result:=GetOutputDirectory;
if (not FilenameIsAbsolute(Result)) and (not IsVirtual) then
Result:=Directory;
Result:=AppendPathDelim(Result)+ChangeFileExt(GetCompileSourceFilename,'.compiled');
end;
function TProject.GetCompileSourceFilename: string;
begin
if MainUnitID<0 then
Result:=''
else
Result:=ExtractFilename(MainUnitInfo.Filename);
end;
function TProject.GetWriteConfigFilePath: string;
begin
Result:=CompilerOptions.ParsedOpts.GetParsedValue(pcosWriteConfigFilePath);
end;
procedure TProject.AutoAddOutputDirToIncPath;
begin
if pfLRSFilesInOutputDirectory in Flags then begin
// the .lrs files are auto created in the output directory
// => make sure the project output directory is in the include path
if SearchDirectoryInSearchPath(CompilerOptions.IncludePath,'$(ProjOutDir)')<1 then
CompilerOptions.MergeToIncludePaths(';$(ProjOutDir)');
end;
end;
function TProject.ExtendUnitSearchPath(NewUnitPaths: string): boolean;
var
CurUnitPaths: String;
r: TModalResult;
begin
CurUnitPaths:=CompilerOptions.ParsedOpts.GetParsedValue(pcosUnitPath);
NewUnitPaths:=RemoveSearchPaths(NewUnitPaths,CurUnitPaths);
if NewUnitPaths<>'' then begin
NewUnitPaths:=CreateRelativeSearchPath(NewUnitPaths,Directory);
r:=IDEMessageDialog(lisExtendUnitPath,
Format(lisExtendUnitSearchPathOfProjectWith, [#13, NewUnitPaths]),
mtConfirmation, [mbYes, mbNo, mbCancel]);
case r of
mrYes: CompilerOptions.MergeToUnitPaths(NewUnitPaths);
mrNo: ;
else exit(false);
end;
end;
Result:=true;
end;
function TProject.ExtendIncSearchPath(NewIncPaths: string): boolean;
var
CurIncPaths: String;
r: TModalResult;
begin
CurIncPaths:=CompilerOptions.ParsedOpts.GetParsedValue(pcosIncludePath);
NewIncPaths:=RemoveSearchPaths(NewIncPaths,CurIncPaths);
if NewIncPaths<>'' then begin
NewIncPaths:=CreateRelativeSearchPath(NewIncPaths,Directory);
r:=IDEMessageDialog(lisExtendIncludePath,
Format(lisExtendIncludeFilesSearchPathOfProjectWith, [#13, NewIncPaths]),
mtConfirmation, [mbYes, mbNo, mbCancel]);
case r of
mrYes: CompilerOptions.MergeToIncludePaths(NewIncPaths);
mrNo: ;
else exit(false);
end;
end;
Result:=true;
end;
function TProject.LoadStateFile(IgnoreErrors: boolean): TModalResult;
var
XMLConfig: TXMLConfig;
StateFile: String;
CurStateFileAge: Integer;
begin
StateFile:=GetStateFilename;
if (not FilenameIsAbsolute(StateFile)) or (not FileExistsUTF8(StateFile)) then
begin
if ConsoleVerbosity>=0 then
DebugLn('TProject.DoLoadStateFile Statefile not found: ',StateFile);
StateFlags:=StateFlags-[lpsfStateFileLoaded];
Result:=mrOk;
exit;
end;
// read the state file
CurStateFileAge:=FileAgeCached(StateFile);
if (not (lpsfStateFileLoaded in StateFlags))
or (StateFileDate<>CurStateFileAge) then
begin
StateFlags:=StateFlags-[lpsfStateFileLoaded];
try
XMLConfig:=TCodeBufXMLConfig.CreateWithCache(StateFile);
try
LastCompilerFilename:=XMLConfig.GetValue('Compiler/Value','');
LastCompilerFileDate:=XMLConfig.GetValue('Compiler/Date',0);
LastCompilerParams.Clear;
SplitCmdLineParams(XMLConfig.GetValue('Params/Value',''),LastCompilerParams);
LastCompileComplete:=XMLConfig.GetValue('Complete/Value',true);
finally
XMLConfig.Free;
end;
StateFileDate:=CurStateFileAge;
except
on E: Exception do begin
if IgnoreErrors then begin
Result:=mrOk;
end else begin
Result:=IDEMessageDialog(lisPkgMangErrorReadingFile,
Format(lisProjMangUnableToReadStateFileOfProjectError,
[StateFile, IDAsString, LineEnding, E.Message]),
mtError,[mbAbort]);
end;
exit;
end;
end;
StateFlags:=StateFlags+[lpsfStateFileLoaded];
end;
Result:=mrOk;
end;
function TProject.SaveStateFile(const CompilerFilename: string;
CompilerParams: TStrings; Complete: boolean): TModalResult;
var
XMLConfig: TXMLConfig;
StateFile: String;
CompilerFileDate: Integer;
begin
StateFile:=GetStateFilename;
if not FilenameIsAbsolute(StateFile) then exit(mrOk);
try
CompilerFileDate:=FileAgeCached(CompilerFilename);
XMLConfig:=TCodeBufXMLConfig.CreateWithCache(StateFile,false);
try
// always write all values for easy use by other tools and other versions of IDE
XMLConfig.SetValue('Compiler/Value',CompilerFilename);
XMLConfig.SetValue('Compiler/Date',CompilerFileDate);
XMLConfig.SetValue('Params/Value',MergeCmdLineParams(CompilerParams));
XMLConfig.SetDeleteValue('Complete/Value',Complete,true);
InvalidateFileStateCache(StateFile);
XMLConfig.Flush;
finally
XMLConfig.Free;
end;
LastCompilerFilename:=CompilerFilename;
LastCompilerFileDate:=CompilerFileDate;
LastCompilerParams:=CompilerParams;
LastCompileComplete:=Complete;
StateFileDate:=FileAgeCached(StateFile);
StateFlags:=StateFlags+[lpsfStateFileLoaded];
except
on E: Exception do begin
Result:=IDEMessageDialog(lisPkgMangErrorWritingFile,
Format(lisProjMangUnableToWriteStateFileForProjectError,
[IDAsString, LineEnding, E.Message]),
mtError,[mbCancel]);
exit;
end;
end;
Result:=mrOk;
end;
procedure TProject.UpdateAllCustomHighlighter;
var
i: Integer;
begin
if IDEEditorOptions=nil then exit;
for i:=0 to UnitCount-1 do
Units[i].UpdateHasCustomHighlighter(FilenameToLazSyntaxHighlighter(Units[i].Filename));
end;
procedure TProject.UpdateAllSyntaxHighlighter;
var
i: Integer;
begin
if IDEEditorOptions=nil then exit;
for i:=0 to UnitCount-1 do
Units[i].UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(Units[i].Filename));
end;
function TProject.GetPOOutDirectory: string;
begin
Result:=POOutputDirectory;
if not IDEMacros.SubstituteMacros(Result) then
debugln(['TProject.GetPOOutDirectory failed POOutputDirectory="',POOutputDirectory,'"']);
Result:=TrimFilename(Result);
if not FilenameIsAbsolute(Result) then
Result:=TrimFilename(AppendPathDelim(Directory)+Result);
end;
function TProject.GetAutoCreatedFormsList: TStrings;
var
i, j: integer;
S, StartS, EndS: String;
begin
if (MainUnitID >= 0) then
begin
Result := CodeToolBoss.ListAllCreateFormStatements(MainUnitInfo.Source);
if Result <> nil then
for i := 0 to Result.Count - 1 do
begin
S := Result[i];
j := Pos(':', S);
if j > 0 then begin
StartS := Copy(S, 1, j-1);
EndS := Copy(S, j+1, Length(S)-j);
if CompareText('t'+StartS, EndS) = 0 then
Result[i] := StartS;
end;
end;// shorten lines of type 'FormName:TFormName' to simply 'FormName'
end
else
Result := nil;
end;
function TProject.AddBookmark(X, Y, ID: Integer; AUnitInfo:TUnitInfo): integer;
begin
Result := Bookmarks.Add(X, Y, ID, AUnitInfo);
SessionModified := true;
end;
procedure TProject.DeleteBookmark(ID: Integer);
var
i: Integer;
begin
i := Bookmarks.IndexOfID(ID);
if i < 0 then exit;
Bookmarks.Delete(i);
SessionModified := true;
end;
procedure TProject.UnitNameChangeHandler(AnUnitInfo: TUnitInfo;
const OldUnitName, NewUnitName: string; CheckIfAllowed: boolean;
var Allowed: boolean);
var
i:integer;
begin
if AnUnitInfo.IsPartOfProject then
begin
if CheckIfAllowed then begin
// check if no other project unit has this name
for i:=0 to UnitCount-1 do begin
if (Units[i].IsPartOfProject)
and (Units[i]<>AnUnitInfo) and (Units[i].Unit_Name<>'')
and (CompareText(Units[i].Unit_Name,NewUnitName)=0) then begin
Allowed:=false;
exit;
end;
end;
end;
if (OldUnitName<>'') then
begin
if (pfMainUnitIsPascalSource in Flags) then
begin
// rename unit in program uses section
CodeToolBoss.RenameUsedUnit(MainUnitInfo.Source, OldUnitName, NewUnitName, '');
end;
if MainUnitInfo = AnUnitInfo then
begin
// we are renaming a project => update resource directives
ProjResources.RenameDirectives(OldUnitName, NewUnitName);
end;
end;
end;
end;
procedure TProject.SetActiveBuildMode(const AValue: TProjectBuildMode);
begin
// Must be set even if FActiveBuildMode=AValue. Modes may be added and deleted,
// the same old address can be used by a new mode.
FActiveBuildMode:=AValue;
if FActiveBuildMode<>nil then
FLazCompilerOptions:=FActiveBuildMode.CompilerOptions
else
FLazCompilerOptions:=nil;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetActiveBuildMode ']);
{$ENDIF}
SessionModified:=true;
if Self=Project1 then
IncreaseBuildMacroChangeStamp;
end;
procedure TProject.SetActiveBuildModeID(aIdent: string);
var
i: Integer;
begin
for i:=0 to BuildModes.Count-1 do
begin
if BuildModes[i].Identifier=aIdent then
begin
ActiveBuildMode:=BuildModes[i];
Break;
end;
end;
end;
procedure TProject.SetAutoOpenDesignerFormsDisabled(const AValue: boolean);
begin
if FAutoOpenDesignerFormsDisabled=AValue then exit;
FAutoOpenDesignerFormsDisabled:=AValue;
end;
procedure TProject.SetEnableI18NForLFM(const AValue: boolean);
begin
if FEnableI18NForLFM=AValue then exit;
FEnableI18NForLFM:=AValue;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetEnableI18NForLFM ',AValue]);
{$ENDIF}
Modified:=true;
end;
procedure TProject.SetLastCompilerParams(AValue: TStrings);
begin
if FLastCompilerParams=AValue then Exit;
//debugln(['TProject.SetLastCompilerParams Old="',FLastCompilerParams,'"']);
//debugln(['TProject.SetLastCompilerParams New="',AValue,'"']);
FLastCompilerParams.Assign(AValue);
end;
procedure TProject.SetMainProject(const AValue: boolean);
begin
if MainProject=AValue then exit;
FMainProject:=AValue;
if MainProject then
SourceDirectories.AddFilename(VirtualDirectory)
else
SourceDirectories.RemoveFilename(VirtualDirectory);
end;
procedure TProject.SetSkipCheckLCLInterfaces(const AValue: boolean);
begin
if FSkipCheckLCLInterfaces=AValue then exit;
FSkipCheckLCLInterfaces:=AValue;
SessionModified:=true;
end;
procedure TProject.SetStorePathDelim(const AValue: TPathDelimSwitch);
begin
if FStorePathDelim=AValue then exit;
FStorePathDelim:=AValue;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetStorePathDelim ']);
{$ENDIF}
Modified:=true;
end;
function TProject.JumpHistoryCheckPosition(
APosition: TProjectJumpHistoryPosition): boolean;
var i: integer;
begin
i:=IndexOfFilename(APosition.Filename);
Result:=(i>=0) and (Units[i].OpenEditorInfoCount > 0);
end;
function TProject.SomethingModified(CheckData, CheckSession: boolean;
Verbose: boolean): boolean;
begin
Result := True;
if CheckData and SomeDataModified(Verbose) then exit;
if CheckSession and SomeSessionModified(Verbose) then exit;
Result := False;
end;
function TProject.SomeDataModified(Verbose: boolean): boolean;
var
AnUnitInfo: TUnitInfo;
begin
Result:=true;
if Modified then
begin
if Verbose then
DebugLn('TProject.SomeDataModified Modified');
Exit;
end;
if BuildModes.IsModified(false) then
begin
if Verbose then
DebugLn(['TProject.SomeDataModified CompilerOptions/BuildModes']);
Exit;
end;
AnUnitInfo:=FirstPartOfProject;
while AnUnitInfo<>nil do begin
if AnUnitInfo.Modified then
begin
if Verbose then
DebugLn('TProject.SomeDataModified PartOfProject ',AnUnitInfo.Filename);
Exit;
end;
AnUnitInfo:=AnUnitInfo.NextPartOfProject;
end;
Result:=false;
end;
function TProject.SomeSessionModified(Verbose: boolean): boolean;
var
i: Integer;
begin
Result:=true;
if SessionModified then
begin
if Verbose then
DebugLn('TProject.SomeSessionModified SessionModified');
Exit;
end;
if BuildModes.IsModified(true) then
begin
if Verbose then
DebugLn(['TProject.SomeSessionModified CompilerOptions/BuildModes']);
Exit;
end;
for i := 0 to UnitCount - 1 do
begin
if Units[i].SessionModified then
begin
if Verbose then
DebugLn('TProject.SomeSessionModified Session of ',Units[i].Filename);
exit;
end;
if (not Units[i].IsPartOfProject) and Units[i].Modified then
begin
if Verbose then
DebugLn('TProject.SomeSessionModified Not PartOfProject ',Units[i].Filename);
exit;
end;
end;
Result:=false;
end;
procedure TProject.MainSourceFilenameChanged;
begin
end;
function TProject.UnitWithComponent(AComponent: TComponent): TUnitInfo;
begin
Result:=fFirst[uilWithComponent];
while (Result<>nil) and (Result.Component<>AComponent) do
Result:=Result.fNext[uilWithComponent];
end;
function TProject.UnitWithComponentClass(AClass: TComponentClass): TUnitInfo;
begin
Result:=fFirst[uilWithComponent];
while (Result<>nil) and (Result.Component.ClassType<>AClass) do
Result:=Result.fNext[uilWithComponent];
end;
function TProject.UnitWithComponentClassName(const AClassName: string): TUnitInfo;
begin
Result := fFirst[uilWithComponent];
while (Result<>nil) and (CompareText(Result.Component.ClassName, AClassName)<>0) do
Result := Result.fNext[uilWithComponent];
end;
function TProject.UnitWithComponentName(AComponentName: String;
OnlyPartOfProject: boolean): TUnitInfo;
var
i: Integer;
begin
if OnlyPartOfProject then begin
Result := fFirst[uilPartOfProject];
while (Result<>nil)
and (SysUtils.CompareText(Result.ComponentName, AComponentName) <> 0) do
Result := Result.fNext[uilPartOfProject];
end else begin
Result:=nil;
for i:=0 to UnitCount-1 do
if SysUtils.CompareText(Units[i].ComponentName,AComponentName)=0 then
begin
Result:=Units[i];
exit;
end;
end;
end;
function TProject.UnitComponentInheritingFrom(AClass: TComponentClass;
Ignore: TUnitInfo): TUnitInfo;
begin
Result:=fFirst[uilWithComponent];
while (Result<>nil) do begin
if (Result<>Ignore) and Result.Component.InheritsFrom(AClass) then exit;
Result:=Result.fNext[uilWithComponent];
end;
end;
function TProject.UnitUsingComponentUnit(ComponentUnit: TUnitInfo;
Types: TUnitCompDependencyTypes): TUnitInfo;
var
Dependency: TUnitComponentDependency;
begin
Result:=nil;
Dependency:=ComponentUnit.FindUsedByComponentDependency(Types);
if Dependency=nil then exit;
Result:=Dependency.UsedByUnit;
end;
function TProject.UnitComponentIsUsed(ComponentUnit: TUnitInfo;
CheckHasDesigner: boolean): boolean;
begin
if ComponentUnit.Component=nil then exit(false);
if CheckHasDesigner
and (uifComponentUsedByDesigner in ComponentUnit.Flags) then
exit(true);
if (uifComponentIndirectlyUsedByDesigner in ComponentUnit.Flags) then
exit(true);
if ComponentUnit.FindUsedByComponentDependency([ucdtAncestor])<>nil then
exit(true);
if ComponentUnit.FindUsedByComponentDependency([ucdtInlineClass])<>nil then
exit(true);
Result:=false;
end;
function TProject.UnitInfoWithFilename(const AFilename: string): TUnitInfo;
var
i: Integer;
begin
i:=IndexOfFilename(AFilename);
if i>=0 then
Result:=Units[i]
else
Result:=nil;
end;
function TProject.UnitInfoWithFilename(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): TUnitInfo;
function MakeFilenameComparable(const TheFilename: string): string;
begin
Result:=TheFilename;
if (pfsfResolveFileLinks in SearchFlags)
and FilenameIsAbsolute(Result) then
Result:=GetPhysicalFilenameCached(Result,false);
end;
function FindFileInList(ListType: TUnitInfoList): TUnitInfo;
var
BaseFilename: String;
CurBaseFilename: String;
begin
BaseFilename:=MakeFilenameComparable(AFilename);
Result:=fFirst[ListType];
while Result<>nil do begin
CurBaseFilename:=MakeFilenameComparable(Result.Filename);
if CompareFilenames(BaseFilename,CurBaseFilename)=0 then exit;
Result:=Result.fNext[ListType];
end;
end;
var
i: Integer;
begin
if (SearchFlags-[pfsfResolveFileLinks]=[pfsfOnlyEditorFiles]) then
// search only in list of Files with EditorIndex
// There is a list, so we can search much faster
Result:=FindFileInList(uilWithEditorIndex)
else if (SearchFlags-[pfsfResolveFileLinks]=[pfsfOnlyProjectFiles]) then
// search only in list of project files
// There is a list, so we can search much faster
Result:=FindFileInList(uilPartOfProject)
else begin
// slow search
i:=IndexOfFilename(AFilename,SearchFlags);
if i>=0 then
Result:=Units[i]
else
Result:=nil;
end;
end;
function TProject.UnitWithUnitname(const AnUnitname: string): TUnitInfo;
var
i: Integer;
begin
i:=IndexOfUnitWithName(AnUnitName,true,nil);
if i>=0 then
Result:=Units[i]
else
Result:=nil;
end;
function TProject.UnitInfoWithLFMFilename(const AFilename: string): TUnitInfo;
var
i: Integer;
begin
i:=IndexOfLFMFilename(AFilename);
if i>=0 then
Result:=Units[i]
else
Result:=nil;
end;
function TProject.AllEditorsInfoCount: Integer;
begin
Result := FAllEditorsInfoList.Count;
end;
function TProject.EditorInfoWithEditorComponent(AEditor: TSourceEditorInterface): TUnitEditorInfo;
begin
Result := Nil;
FAllEditorsInfoMap.GetData(AEditor, Result);
end;
procedure TProject.EditorInfoAdd(EdInfo: TUnitEditorInfo);
begin
FAllEditorsInfoList.Add(EdInfo);
Assert(not Assigned(EdInfo.EditorComponent),
'TUnitEditorInfo.EditorComponent should not be assigned. It is set later.');
end;
procedure TProject.EditorInfoRemove(EdInfo: TUnitEditorInfo);
begin
FAllEditorsInfoList.Remove(EdInfo);
if Assigned(EdInfo.EditorComponent) then
FAllEditorsInfoMap.Delete(EdInfo.EditorComponent);
end;
procedure TProject.MacroEngineSubstitution(TheMacro: TTransferMacro;
const MacroName: string; var s: string; const Data: PtrInt; var Handled,
Abort: boolean; Depth: integer);
var
Values: TCTCfgScriptVariables;
Macro: PCTCfgScriptVariable;
var
NewValue: String;
begin
if Data=CompilerOptionMacroPlatformIndependent then
begin
NewValue:=GetMakefileMacroValue(MacroName);
if NewValue<>'' then begin
s:=NewValue;
Handled:=true;
exit;
end;
end;
// check build macros
if IsValidIdent(MacroName) then
begin
Values:=GetBuildMacroValues(CompilerOptions,true);
if Values<>nil then begin
Macro:=Values.GetVariable(PChar(MacroName));
if Macro<>nil then
begin
s:=GetCTCSVariableAsString(Macro);
//debugln(['TProject.OnMacroEngineSubstitution Macro=',MacroName,' Value="',s,'"']);
Handled:=true;
exit;
end;
end;
end;
// check local macros
// check global macros
GlobalMacroList.ExecuteMacro(MacroName,s,Data,Handled,Abort,Depth);
end;
function TProject.SearchFile(const ShortFilename: string;
SearchFlags: TSearchIDEFileFlags): TUnitInfo;
var
SearchedFilename: String;
function FilenameFits(AFilename: string): boolean;
begin
if siffIgnoreExtension in SearchFlags then
AFileName:=ExtractFilenameOnly(AFileName);
if FilenameIsAbsolute(AFileName) then
AFileName:=ExtractFilename(AFileName);
if siffCaseSensitive in SearchFlags then
Result:=SearchedFilename=AFilename
else // check Pascal case insensitivity (CompareText, do not use CompareFilenamesIgnoreCase, because of Turkish I)
Result:=CompareText(SearchedFilename,AFilename)=0;
end;
begin
SearchedFilename:=ShortFilename;
if siffIgnoreExtension in SearchFlags then
SearchedFilename:=ExtractFilenameOnly(SearchedFilename);
// search in files which are part of the project
Result:=FirstPartOfProject;
while Result<>nil do begin
if FilenameFits(Result.Filename) then exit;
Result:=Result.NextPartOfProject;
end;
// search in files opened in editor
if not (siffDoNotCheckOpenFiles in SearchFlags) then begin
Result:=FirstUnitWithEditorIndex;
while Result<>nil do begin
if FilenameFits(Result.Filename) then exit;
Result:=Result.NextUnitWithEditorIndex;
end;
end;
end;
function TProject.FindFile(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): TLazProjectFile;
begin
Result:=UnitInfoWithFilename(AFilename, SearchFlags);
end;
function TProject.UpdateIsPartOfProjectFromMainUnit: TModalResult;
var
FoundInUnits, MissingInUnits, NormalUnits: TStrings;
i: Integer;
Code: TCodeBuffer;
CurFilename: String;
AnUnitInfo, NewUnitInfo: TUnitInfo;
begin
if (MainUnitID<0) or (MainUnitInfo.Source=nil)
or ([pfMainUnitIsPascalSource,pfMainUnitHasUsesSectionForAllUnits]*Flags
<>[pfMainUnitIsPascalSource,pfMainUnitHasUsesSectionForAllUnits])
then
exit(mrOk);
try
if CodeToolBoss.FindDelphiProjectUnits(MainUnitInfo.Source,FoundInUnits,
MissingInUnits, NormalUnits, true)
then
Result:=mrOk
else
Result:=mrCancel;
if FoundInUnits<>nil then begin
for i:=0 to FoundInUnits.Count-1 do begin
Code:=FoundInUnits.Objects[i] as TCodeBuffer;
CurFilename:=Code.Filename;
AnUnitInfo:=UnitInfoWithFilename(CurFilename);
if (AnUnitInfo<>nil) and AnUnitInfo.IsPartOfProject then continue;
if ConsoleVerbosity>=0 then
debugln(['Note: (lazarus) [TProject.UpdateIsPartOfProjectFromMainUnit] used unit ',FoundInUnits[i],' not marked in lpi. Setting IsPartOfProject flag.']);
if AnUnitInfo=nil then begin
NewUnitInfo:=TUnitInfo.Create(nil);
NewUnitInfo.Filename:=CurFilename;
NewUnitInfo.IsPartOfProject:=true;
NewUnitInfo.Source:=Code;
AddFile(NewUnitInfo,false);
end else
AnUnitInfo.IsPartOfProject:=true;
end;
end;
finally
FoundInUnits.Free;
MissingInUnits.Free;
NormalUnits.Free;
end;
end;
function TProject.IndexOfFilename(const AFilename: string): integer;
begin
Result:=UnitCount-1;
while (Result>=0) do begin
if CompareFilenames(AFilename,Units[Result].Filename)=0 then exit;
dec(Result);
end;
end;
function TProject.IndexOfFilename(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): integer;
function MakeFilenameComparable(const TheFilename: string): string;
begin
Result:=TheFilename;
if (pfsfResolveFileLinks in SearchFlags)
and (FilenameIsAbsolute(Result)) then
Result:=GetPhysicalFilenameCached(Result,false);
end;
var
BaseFilename: String;
CurBaseFilename: String;
begin
BaseFilename:=MakeFilenameComparable(AFilename);
Result:=UnitCount-1;
while (Result>=0) do begin
if (pfsfOnlyEditorFiles in SearchFlags)
and (Units[Result].OpenEditorInfoCount = 0) then begin
dec(Result);
continue;
end;
if (pfsfOnlyVirtualFiles in SearchFlags)
and (not Units[Result].IsVirtual) then begin
dec(Result);
continue;
end;
if (pfsfOnlyProjectFiles in SearchFlags)
and (not Units[Result].IsPartOfProject) then begin
dec(Result);
continue;
end;
CurBaseFilename:=MakeFilenameComparable(Units[Result].Filename);
if CompareFilenames(BaseFilename,CurBaseFilename)=0 then exit;
dec(Result);
end;
end;
function TProject.IndexOfLFMFilename(const AFilename: string): integer;
var
CurUnit: TUnitInfo;
begin
Result:=UnitCount-1;
while (Result>=0) do begin
CurUnit:=Units[Result];
if (CurUnit.SourceLFM<>nil)
and (CompareFilenames(AFilename,CurUnit.SourceLFM.Filename)=0) then exit;
dec(Result);
end;
end;
function TProject.ProjectUnitWithFilename(const AFilename: string): TUnitInfo;
begin
Result:=fFirst[uilPartOfProject];
while Result<>nil do begin
if CompareFileNames(AFilename,Result.Filename)=0 then exit;
Result:=Result.fNext[uilPartOfProject];
end;
end;
function TProject.ProjectUnitWithShortFilename(const ShortFilename: string): TUnitInfo;
begin
Result:=fFirst[uilPartOfProject];
while Result<>nil do begin
if CompareFileNames(ShortFilename,ExtractFilename(Result.Filename))=0 then
exit;
Result:=Result.fNext[uilPartOfProject];
end;
end;
function TProject.ProjectUnitWithUnitname(const AnUnitName: string): TUnitInfo;
begin
Result:=fFirst[uilPartOfProject];
while Result<>nil do begin
if CompareText(AnUnitName,Result.Unit_Name)=0 then exit;
Result:=Result.fNext[uilPartOfProject];
end;
end;
procedure TProject.UpdateFileBuffer;
begin
fProjectInfoFileBuffer:=CodeToolBoss.LoadFile(ProjectInfoFile,true,true);
fProjectInfoFileDate:=FileAgeCached(ProjectInfoFile);
if fProjectInfoFileBuffer<>nil then
fProjectInfoFileBufChangeStamp:=fProjectInfoFileBuffer.ChangeStep
else
fProjectInfoFileBufChangeStamp:=LUInvalidChangeStamp;
end;
procedure TProject.UpdateProjectDirectory;
var
i: Integer;
begin
if fDestroying then exit;
fProjectDirectory:=ExtractFilePath(fProjectInfoFile);
if BuildModes<>nil then
for i:=0 to BuildModes.Count-1 do
BuildModes[i].CompilerOptions.BaseDirectory:=fProjectDirectory;
if fProjectDirectory<>fProjectDirectoryReferenced then begin
if fProjectDirectoryReferenced<>'' then
FSourceDirectories.RemoveFilename(fProjectDirectoryReferenced);
if fProjectDirectory<>'' then
FSourceDirectories.AddFilename(fProjectDirectory);
fProjectDirectoryReferenced:=fProjectDirectory;
end;
end;
procedure TProject.UpdateSessionFilename;
begin
case SessionStorage of
pssInProjectInfo: ProjectSessionFile:=ProjectInfoFile;
pssInProjectDir: ProjectSessionFile:=ChangeFileExt(ProjectInfoFile,'.lps');
pssInIDEConfig: ProjectSessionFile:=AppendPathDelim(GetProjectSessionsConfigPath)
+ExtractFileNameOnly(ProjectInfoFile)+'.lps';
pssNone: ProjectSessionFile:='';
end;
end;
procedure TProject.UpdateSourceDirectories;
var
Cnt: Integer;
i: Integer;
AnUnitInfo: TUnitInfo;
begin
Cnt:=FUnitList.Count;
for i:=0 to Cnt-1 do begin
AnUnitInfo:=Units[i];
AnUnitInfo.FSourceDirectoryReferenced:=false;
end;
ClearSourceDirectories;
for i:=0 to Cnt-1 do begin
AnUnitInfo:=Units[i];
AnUnitInfo.AutoReferenceSourceDir:=true;
AnUnitInfo.UpdateSourceDirectoryReference;
end;
//DebugLn('TProject.UpdateSourceDirectories B ',UnitCount,' "',fSourceDirectories.CreateSearchPathFromAllFiles,'"');
end;
procedure TProject.UpdateUsageCounts(const ConfigFilename: string);
var
UnitUsageCount: TDateTime;
DiffTime: TDateTime;
i: Integer;
begin
UnitUsageCount:=0;
if CompareFileNames(ConfigFilename,fLastReadLPIFilename)=0 then begin
DiffTime:=Now-fLastReadLPIFileDate;
if DiffTime>0 then
UnitUsageCount:= DiffTime*24; // one step every hour
fLastReadLPIFileDate:=Now;
end;
for i:=0 to UnitCount-1 do begin
if Units[i].IsPartOfProject then
Units[i].UpdateUsageCount(uuIsPartOfProject,UnitUsageCount)
else if Units[i].Loaded then
Units[i].UpdateUsageCount(uuIsLoaded,UnitUsageCount)
else
Units[i].UpdateUsageCount(uuNotUsed,UnitUsageCount);
end;
end;
function TProject.UnitMustBeSaved(UnitInfo: TUnitInfo; WriteFlags: TProjectWriteFlags;
SaveSession: boolean): boolean;
begin
Result:=false;
if not UnitInfo.IsPartOfProject then begin
if not SaveSession then exit;
if (pfSaveOnlyProjectUnits in Flags) then exit;
if (pwfSaveOnlyProjectUnits in WriteFlags) then exit;
if (not UnitInfo.Loaded) then begin
if (not (pfSaveClosedUnits in Flags)) then exit;
if (pwfSkipClosedUnits in WriteFlags) then exit;
if UnitInfo.fUsageCount<=0 then exit;
end;
end;
Result:=true;
end;
procedure TProject.UpdateVisibleEditor(PgIndex: integer);
var
i: Integer;
begin
i := AllEditorsInfoCount - 1;
while i >= 0 do begin
if (AllEditorsInfo[i].PageIndex = PgIndex) then
AllEditorsInfo[i].IsVisibleTab := True;
dec(i);
end;
end;
procedure TProject.LoadDefaultSession;
var
AnUnitInfo: TUnitInfo;
BestUnitInfo: TUnitInfo;
begin
BestUnitInfo:=FirstUnitWithEditorIndex;
if (BestUnitInfo<>nil) and (BestUnitInfo.Loaded)
and FileExistsCached(BestUnitInfo.Filename) then
exit;
BestUnitInfo:=nil;
if (MainUnitID>=0) then begin
if Requires(PackageGraph.LCLPackage,true)
and (Flags*[pfMainUnitHasCreateFormStatements,pfMainUnitHasTitleStatement,pfMainUnitHasScaledStatement]<>[])
then begin
// this is a probably a LCL project where the main source only contains
// automatic code
end else
BestUnitInfo:=MainUnitInfo;
end;
if BestUnitInfo=nil then begin
AnUnitInfo:=FirstPartOfProject;
while AnUnitInfo<>nil do begin
if FileExistsCached(AnUnitInfo.Filename) then begin
if (BestUnitInfo=nil)
or (FilenameHasPascalExt(AnUnitInfo.Filename)
and (not FilenameHasPascalExt(BestUnitInfo.Filename)))
then begin
BestUnitInfo:=AnUnitInfo;
end;
end;
AnUnitInfo:=AnUnitInfo.NextPartOfProject;
end;
end;
if BestUnitInfo<>nil then begin
BestUnitInfo.EditorInfo[0].PageIndex := 0;
BestUnitInfo.EditorInfo[0].WindowID := 0;
BestUnitInfo.EditorInfo[0].IsVisibleTab := True;
ActiveWindowIndexAtStart:=0;
BestUnitInfo.Loaded:=true;
end;
end;
procedure TProject.ClearSourceDirectories;
begin
FSourceDirectories.Clear;
fProjectDirectoryReferenced:='';
if MainProject then
FSourceDirectories.AddFilename(VirtualDirectory);
if (fProjectDirectory<>'') then begin
FSourceDirectories.AddFilename(fProjectDirectory);
fProjectDirectoryReferenced:=fProjectDirectory;
end;
end;
procedure TProject.SourceDirectoriesChanged(Sender: TObject);
begin
FDefineTemplates.SourceDirectoriesChanged;
end;
function TProject.GetDefineTemplates: TProjPackDefineTemplates;
begin
Result:=FDefineTemplates;
end;
function TProject.GetMainFile: TLazProjectFile;
begin
Result:=MainUnitInfo;
end;
function TProject.GetMainFileID: Integer;
begin
Result:=MainUnitID;
end;
procedure TProject.SetMainFileID(const AValue: Integer);
begin
MainUnitID:=AValue;
end;
function TProject.GetLazBuildModes: TLazProjectBuildModes;
begin
Result:=FBuildModes;
end;
procedure TProject.AddToList(AnUnitInfo: TUnitInfo; ListType: TUnitInfoList);
begin
// add to list if AnUnitInfo is not in list
if (fFirst[ListType]<>AnUnitInfo)
and (AnUnitInfo.fNext[ListType]=nil)
and (AnUnitInfo.fPrev[ListType]=nil) then begin
AnUnitInfo.fPrev[ListType]:=fLast[ListType];
AnUnitInfo.fNext[ListType]:=nil;
if fFirst[ListType]=nil then
fFirst[ListType]:=AnUnitInfo
else
fLast[ListType].fNext[ListType]:=AnUnitInfo;
fLast[ListType]:=AnUnitInfo;
end;
end;
procedure TProject.RemoveFromList(AnUnitInfo: TUnitInfo; ListType: TUnitInfoList);
begin
// remove from list if AnUnitInfo is in list
if fFirst[ListType]=AnUnitInfo then
fFirst[ListType]:=AnUnitInfo.fNext[ListType];
if fLast[ListType]=AnUnitInfo then
fLast[ListType]:=AnUnitInfo.fPrev[ListType];
if AnUnitInfo.fNext[ListType]<>nil then
AnUnitInfo.fNext[ListType].fPrev[ListType]:=AnUnitInfo.fPrev[ListType];
if AnUnitInfo.fPrev[ListType]<>nil then
AnUnitInfo.fPrev[ListType].fNext[ListType]:=AnUnitInfo.fNext[ListType];
AnUnitInfo.fNext[ListType]:=nil;
AnUnitInfo.fPrev[ListType]:=nil;
end;
{ TProjectCompilationToolOptions }
constructor TProjectCompilationToolOptions.Create(TheOwner: TLazCompilerOptions);
begin
inherited Create(TheOwner);
FDefaultCompileReasons:=crAll;
end;
function TProjectCompilationToolOptions.CreateDiff(
CompOpts: TCompilationToolOptions; Tool: TCompilerDiffTool): boolean;
begin
Assert(Assigned(Tool),'TProjectCompilationToolOptions.CreateDiff: Tool=Nil.');
Result:=AddCompileReasonsDiff('CompileReasons', CompileReasons,
CompOpts.CompileReasons, Tool);
if Result then exit;
if inherited CreateDiff(CompOpts, Tool) then Result:=true;
end;
procedure TProjectCompilationToolOptions.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string; DoSwitchPathDelims: boolean);
begin
inherited LoadFromXMLConfig(XMLConfig, Path, DoSwitchPathDelims);
CompileReasons := LoadXMLCompileReasons(XMLConfig, Path+'CompileReasons/',
DefaultCompileReasons);
//debugln(['TProjectCompilationToolOptions.LoadFromXMLConfig ',Path,' ',crCompile in CompileReasons]);
end;
procedure TProjectCompilationToolOptions.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; UsePathDelim: TPathDelimSwitch);
begin
inherited SaveToXMLConfig(XMLConfig, Path, UsePathDelim);
SaveXMLCompileReasons(XMLConfig, Path+'CompileReasons/', CompileReasons,
DefaultCompileReasons);
//debugln(['TProjectCompilationToolOptions.SaveToXMLConfig ',Path,' ',crCompile in CompileReasons]);
end;
function TProjectCompilationToolOptions.GetProject: TProject;
begin
if (Owner is TProjectCompilerOptions) then
Result:=TProjectCompilerOptions(Owner).LazProject
else
Result:=nil;
end;
procedure TProjectCompilationToolOptions.SetCompileReasons(const AValue: TCompileReasons);
begin
if FCompileReasons=AValue then exit;
FCompileReasons:=AValue;
Owner.IncreaseChangeStamp;
end;
procedure TProjectCompilationToolOptions.SetDefaultCompileReasons(const AValue: TCompileReasons);
begin
if FDefaultCompileReasons=AValue then exit;
FDefaultCompileReasons:=AValue;
Owner.IncreaseChangeStamp;
end;
procedure TProjectCompilationToolOptions.SubstituteMacros(var s: string);
var
CompOpts: TProjectCompilerOptions;
begin
if Owner is TProjectCompilerOptions then begin
CompOpts:=TProjectCompilerOptions(Owner);
//debugln(['TProjectCompilationToolOptions.SubstituteMacros ',DbgSName(Owner),' ',CompOpts.LazProject<>nil]);
s:=CompOpts.SubstituteProjectMacros(s,false);
end else
inherited SubstituteMacros(s);
end;
{ TProjectCompilerOptions }
procedure TProjectCompilerOptions.LoadFromXMLConfig(AXMLConfig: TXMLConfig;
const Path: string);
begin
inherited LoadFromXMLConfig(AXMLConfig,Path);
//FileVersion:=aXMLConfig.GetValue(Path+'Version/Value', 0);
FCompileReasons := LoadXMLCompileReasons(AXMLConfig,Path+'CompileReasons/',crAll);
end;
procedure TProjectCompilerOptions.SaveToXMLConfig(AXMLConfig: TXMLConfig;
const Path: string);
begin
inherited SaveToXMLConfig(AXMLConfig,Path);
SaveXMLCompileReasons(AXMLConfig, Path+'CompileReasons/', FCompileReasons, crAll);
end;
procedure TProjectCompilerOptions.SetTargetCPU(const AValue: string);
begin
inherited SetTargetCPU(AValue);
end;
procedure TProjectCompilerOptions.SetTargetOS(const AValue: string);
begin
inherited SetTargetOS(AValue);
end;
procedure TProjectCompilerOptions.SetCustomOptions(const AValue: string);
begin
if CustomOptions=AValue then exit;
inherited SetCustomOptions(AValue);
if IsActive then
LazProject.DefineTemplates.CustomDefinesChanged;
end;
procedure TProjectCompilerOptions.SetIncludePaths(const AValue: string);
begin
if IncludePath=AValue then exit;
inherited SetIncludePaths(AValue);
end;
procedure TProjectCompilerOptions.SetLibraryPaths(const AValue: string);
begin
if Libraries=AValue then exit;
inherited SetLibraryPaths(AValue);
end;
procedure TProjectCompilerOptions.SetLinkerOptions(const AValue: string);
begin
if LinkerOptions=AValue then exit;
inherited SetLinkerOptions(AValue);
end;
procedure TProjectCompilerOptions.SetNamespaces(const AValue: string);
begin
if Namespaces=AValue then exit;
inherited SetNamespaces(AValue);
end;
procedure TProjectCompilerOptions.SetObjectPath(const AValue: string);
begin
if ObjectPath=AValue then exit;
inherited SetObjectPath(AValue);
end;
procedure TProjectCompilerOptions.SetSrcPath(const AValue: string);
begin
if SrcPath=AValue then exit;
inherited SetSrcPath(AValue);
end;
procedure TProjectCompilerOptions.SetUnitPaths(const AValue: string);
begin
if OtherUnitFiles=AValue then exit;
inherited SetUnitPaths(AValue);
end;
procedure TProjectCompilerOptions.SetUnitOutputDir(const AValue: string);
begin
if UnitOutputDirectory=AValue then exit;
inherited SetUnitOutputDir(AValue);
if IsActive then
LazProject.DefineTemplates.OutputDirectoryChanged;
end;
procedure TProjectCompilerOptions.SetConditionals(AValue: string);
begin
AValue:=UTF8Trim(AValue,[]);
if Conditionals=AValue then exit;
inherited SetConditionals(AValue);
end;
function TProjectCompilerOptions.SubstituteProjectMacros(s: string;
PlatformIndependent: boolean): string;
// Don't use "const" for s parameter.
begin
Result:=s;
if LazProject=nil then exit;
//debugln(['TProjectCompilerOptions.SubstituteProjectMacros s="',s,'"']);
if PlatformIndependent then begin
if not LazProject.MacroEngine.SubstituteStr(Result,CompilerOptionMacroPlatformIndependent)
then
debugln(['TProjectCompilerOptions.SubstituteProjectMacros failed: "',CompilerOptionMacroPlatformIndependent,'"']);
end
else begin
if not LazProject.MacroEngine.SubstituteStr(Result,CompilerOptionMacroNormal)
then
debugln(['TProjectCompilerOptions.SubstituteProjectMacros failed: "',CompilerOptionMacroNormal,'"']);
end;
end;
procedure TProjectCompilerOptions.Assign(Source: TPersistent);
var
ProjCompOptions: TProjectCompilerOptions;
begin
inherited Assign(Source);
if Source is TProjectCompilerOptions then begin
ProjCompOptions:=TProjectCompilerOptions(Source);
FCompileReasons:=ProjCompOptions.FCompileReasons;
end else begin
FCompileReasons:=[crCompile, crBuild, crRun];
// keep BuildModes
end;
end;
function TProjectCompilerOptions.CreateDiff(CompOpts: TBaseCompilerOptions;
Tool: TCompilerDiffTool): boolean;
begin
//if Tool<>nil then debugln(['TProjectCompilerOptions.CreateDiff ',DbgSName(Self)]);
if (CompOpts is TProjectCompilerOptions) then begin
Result:=AddCompileReasonsDiff('CompileReasons',FCompileReasons,
TProjectCompilerOptions(CompOpts).FCompileReasons,Tool);
end else begin
Result:=true;
if Tool<>nil then Tool.Differ:=true;
end;
//if Tool<>nil then debugln(['TProjectCompilerOptions.CreateDiff Before inherited ',Result]);
if (Tool=nil) and Result then exit;
if (inherited CreateDiff(CompOpts, Tool)) then
Result:=true;
end;
procedure TProjectCompilerOptions.BeforeReadExec(Sender: TObject);
begin
if LazProject<>nil then
LazProject.BackupBuildModes;
end;
procedure TProjectCompilerOptions.AfterWriteExec(Sender: TObject; Restore: boolean);
begin
if Restore and (LazProject<>nil) then
LazProject.RestoreBuildModes;
end;
procedure TProjectCompilerOptions.SetAlternativeCompile(const Command: string;
ScanFPCMsgs: boolean);
begin
inherited SetAlternativeCompile(Command, ScanFPCMsgs);
CompileReasons:=[];
end;
class function TProjectCompilerOptions.GetInstance: TAbstractIDEOptions;
begin
Result := Project1.CompilerOptions;
end;
class function TProjectCompilerOptions.GetGroupCaption: string;
begin
Result := dlgCompilerOptions;
end;
constructor TProjectCompilerOptions.Create(const AOwner: TObject);
begin
FCompileReasons := crAll;
inherited Create(AOwner, TProjectCompilationToolOptions);
if AOwner <> nil then
FProject := AOwner as TProject;
ParsedOpts.OnLocalSubstitute:=@SubstituteProjectMacros;
OnAfterWrite:=@AfterWriteExec;
OnBeforeRead:=@BeforeReadExec;
end;
destructor TProjectCompilerOptions.Destroy;
begin
inherited Destroy;
end;
function TProjectCompilerOptions.IsActive: boolean;
begin
Result:=(LazProject<>nil)
and not LazProject.BuildModes.Assigning
and (LazProject.CompilerOptions=Self);
end;
procedure TProjectCompilerOptions.Clear;
begin
inherited Clear;
end;
function TProjectCompilerOptions.CanBeDefaulForProject: boolean;
begin
Result:=true;
end;
function TProjectCompilerOptions.GetOwnerName: string;
begin
Result:=LazProject.GetTitleOrName;
if Result='' then Result:=ExtractFilename(LazProject.ProjectInfoFile);
end;
function TProjectCompilerOptions.GetDefaultMainSourceFileName: string;
var
MainUnitInfo: TUnitInfo;
begin
MainUnitInfo:=FProject.MainUnitInfo;
if (MainUnitInfo<>nil) then
Result:=ExtractFileName(MainUnitInfo.Filename)
else
Result:='';
if Result='' then
Result:=inherited GetDefaultMainSourceFileName;
end;
function TProjectCompilerOptions.GetDefaultWriteConfigFilePath: string;
begin
Result:='$(ProjOutDir)'+PathDelim+'fpclaz.cfg';
end;
procedure TProjectCompilerOptions.GetInheritedCompilerOptions(
var OptionsList: TFPList);
var
PkgList: TFPList;
ReqFlags: TPkgIntfRequiredFlags;
begin
PkgList:=nil;
try
ReqFlags:=[];
if not (pfUseDesignTimePackages in LazProject.Flags) then
Include(ReqFlags,pirSkipDesignTimeOnly);
LazProject.GetAllRequiredPackages(PkgList,ReqFlags);
OptionsList:=GetUsageOptionsList(PkgList);
finally
PkgList.Free;
end;
end;
{ TProjectDefineTemplates }
constructor TProjectDefineTemplates.Create(AOwner: IProjPack);
begin
inherited Create(AOwner);
end;
destructor TProjectDefineTemplates.Destroy;
begin
inherited Destroy;
end;
procedure TProjectDefineTemplates.UpdateMain;
begin
if (Owner as TProject).Destroying then exit;
// update the package block define template (the container for all other
// define templates of the project)
if FMain=nil then begin
// create the main project template
FMain:=CreateProjectTemplateWithID(Owner.IDAsWord);
FMain.SetDefineOwner(Owner as TProject,false);
FMain.SetFlags([dtfAutoGenerated],[],false);
end else
FMain.Name:=Owner.IDAsWord;
// ClearCache is here unnessary, because it is only a block
end;
function TProjectDefineTemplates.UpdateSrcDirIfDef: Boolean;
// Returns the changed state
var
NamespacesDefTempl: TDefineTemplate;
UnitPathDefTempl: TDefineTemplate;
IncPathDefTempl: TDefineTemplate;
SrcPathDefTempl: TDefineTemplate;
IfValue: String;
begin
// The options are enclosed by an
// IFDEF #ProjectSrcMark<PckId> template.
// Each source directory defines this variable, so that the settings can be
// activated for each source directory by a simple DEFINE.
if (FMain=nil) then UpdateMain;
if FSrcDirectories=nil then begin
FSrcDirectories:=TDefineTemplate.Create('Source Directories',
'Source Directories','','',
da_Block);
FMain.AddChild(FSrcDirectories);
end;
Result:=false;
IfValue:='defined(#ProjectSrcMark'+Owner.IDAsWord+')';
if (Owner as TProject) = Project1 then
IfValue:=IfValue+' or defined('+UseDefaultsFlagName+')';
if FSrcDirIf=nil then begin
FSrcDirIf:=TDefineTemplate.Create('Source Directory Additions',
'Additional defines for project source directories and all directories using defaults',
'',IfValue,
da_If);
FMain.AddChild(FSrcDirIf);
// create namespaces template for this directory
NamespacesDefTempl:=TDefineTemplate.Create('Namespaces', lisPkgDefsNamespaces,
NamespacesMacroName,NamespacesMacro+';$ProjectNamespaces('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(NamespacesDefTempl);
// create unit path template for this directory
UnitPathDefTempl:=TDefineTemplate.Create('UnitPath', lisPkgDefsUnitPath,
UnitPathMacroName,UnitPathMacro+';$ProjectUnitPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(UnitPathDefTempl);
// create include path template for this directory
IncPathDefTempl:=TDefineTemplate.Create('IncPath','Include Path',
IncludePathMacroName,IncludePathMacro+';$ProjectIncPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(IncPathDefTempl);
// create src path template for this directory
SrcPathDefTempl:=TDefineTemplate.Create('SrcPath','Src Path',
SrcPathMacroName,SrcPathMacro+';$ProjectSrcPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(SrcPathDefTempl);
Result:=true;
end else begin
if FSrcDirIf.Value<>IfValue then begin
FSrcDirIf.Value:=IfValue;
Result:=true;
end;
end;
end;
procedure TProjectDefineTemplates.UpdateOutputDirectory;
var
Proj: TProject;
begin
Proj := Owner as TProject;
//DebugLn('TProjectDefineTemplates.UpdateDefinesForOutputDirectory ',Owner.IDAsString);
if (not Owner.NeedsDefineTemplates) or (not Active) then exit;
if FMain=nil then UpdateMain;
if FOutputDir=nil then begin
//DebugLn('TProjectDefineTemplates.UpdateDefinesForOutputDirectory ',Owner.IDAsString,' creating FOutputDir');
FOutputDir:=TDefineTemplate.Create(ProjectOutputDirDefTemplName,
'Output directory of proj', '', Proj.GetOutputDirectory, da_Directory);
FOutputDir.SetDefineOwner(Proj,false);
FOutputDir.SetFlags([dtfAutoGenerated],[],false);
DisableDefaultsInDirectories(FOutputDir,false);
FMain.AddChild(FOutputDir);
FixTemplateOrder;
end else begin
FOutputDir.Value:=Proj.GetOutputDirectory;
end;
if (FOutPutSrcPath=nil)
or (fLastOutputDirSrcPathIDAsString<>Owner.IDAsString) then begin
fLastOutputDirSrcPathIDAsString:=Owner.IDAsString;
FOutputSrcPath:=TDefineTemplate.Create('CompiledSrcPath',
lisPkgDefsCompiledSrcPathAddition, CompiledSrcPathMacroName,
'$ProjectSrcPath('+fLastOutputDirSrcPathIDAsString+');'
+'$('+CompiledSrcPathMacroName+')',
da_Define);
FOutputSrcPath.SetDefineOwner(Proj,false);
FOutputSrcPath.SetFlags([dtfAutoGenerated],[],false);
CodeToolBoss.DefineTree.ReplaceChild(FOutputDir,FOutputSrcPath,FOutputSrcPath.Name);
end;
end;
procedure TProjectDefineTemplates.UpdateSourceDirectories;
var
NewSourceDirs: TStringList;
i: Integer;
SrcDirDefTempl: TDefineTemplate;
IDHasChanged: Boolean;
SrcDirMarkDefTempl: TDefineTemplate;
CurUnitPath: String;
begin
//DebugLn('TProjectDefineTemplates.UpdateDefinesForSourceDirectories ',Owner.IDAsString,' Active=',dbgs(Active),' TimeStamp=',dbgs(fLastSourceDirStamp),' Project.TimeStamp=',dbgs(Project.SourceDirectories.TimeStamp));
if (not Owner.NeedsDefineTemplates) or (not Active) then exit;
// quick check if something has changed
IDHasChanged:=fLastSourceDirsIDAsString<>Owner.IDAsString;
CurUnitPath:=Owner.BaseCompilerOptions.ParsedOpts.GetParsedValue(pcosUnitPath);
CurUnitPath:=CreateAbsoluteSearchPath(CurUnitPath,
Owner.BaseCompilerOptions.BaseDirectory);
//DebugLn('TProjectDefineTemplates.UpdateDefinesForSourceDirectories A');
if (fLastSourceDirectories<>nil)
and (fLastSourceDirStamp=Owner.SourceDirectories.TimeStamp)
and (not IDHasChanged)
and (CurUnitPath=fLastUnitPath) then
exit;
fLastSourceDirStamp:=Owner.SourceDirectories.TimeStamp;
fLastSourceDirsIDAsString:=Owner.IDAsString;
fLastUnitPath:=CurUnitPath;
NewSourceDirs:=Owner.SourceDirectories.CreateFileList;
//DebugLn('TProjectDefineTemplates.UpdateDefinesForSourceDirectories B "',NewSourceDirs.Text,'"');
try
MergeSearchPaths(NewSourceDirs,CurUnitPath);
// real check if something has changed
if (fLastSourceDirectories<>nil)
and (NewSourceDirs.Count=fLastSourceDirectories.Count)
and (not IDHasChanged) then begin
i:=NewSourceDirs.Count-1;
while (i>=0)
and (CompareFilenames(NewSourceDirs[i],fLastSourceDirectories[i])=0) do
dec(i);
if i<0 then exit;
end;
// clear old define templates
if fLastSourceDirectories<>nil then begin
for i:=0 to fLastSourceDirectories.Count-1 do begin
SrcDirDefTempl:=TDefineTemplate(fLastSourceDirectories.Objects[i]);
SrcDirDefTempl.Unbind;
SrcDirDefTempl.Free;
end;
fLastSourceDirectories.Clear;
end else
fLastSourceDirectories:=TStringList.Create;
// build source directory define templates
fLastSourceDirectories.Assign(NewSourceDirs);
if (FSrcDirIf=nil) and (fLastSourceDirectories.Count>0) then
UpdateSrcDirIfDef;
for i:=0 to fLastSourceDirectories.Count-1 do begin
// create directory template
SrcDirDefTempl:=TDefineTemplate.Create('Source Directory '+IntToStr(i+1),
fLastSourceDirectories[i],'',fLastSourceDirectories[i],da_Directory);
DisableDefaultsInDirectories(SrcDirDefTempl,false);
fLastSourceDirectories.Objects[i]:=SrcDirDefTempl;
// add proj source directory marker
SrcDirMarkDefTempl:=TDefineTemplate.Create('ProjectSrcDirMark',
lisProjProjectSourceDirectoryMark, '#ProjectSrcMark'+Owner.IDAsWord,
'1', da_Define);
SrcDirDefTempl.AddChild(SrcDirMarkDefTempl);
SrcDirDefTempl.SetDefineOwner(Owner as TProject, false);
SrcDirDefTempl.SetFlags([dtfAutoGenerated],[],false);
// add directory
FSrcDirectories.AddChild(SrcDirDefTempl);
end;
//DebugLn('TProjectDefineTemplates.UpdateSourceDirectories: Calling CodeToolBoss.DefineTree.ClearCache');
CodeToolBoss.DefineTree.ClearCache;
finally
NewSourceDirs.Free;
end;
end;
procedure TProjectDefineTemplates.UpdateDefinesForCustomDefines;
var
OptionsDefTempl: TDefineTemplate;
NewCustomOptions: String;
begin
if (not Owner.NeedsDefineTemplates) or (not Active) then exit;
// check if something has changed
NewCustomOptions:=Owner.BaseCompilerOptions.GetOptionsForCTDefines;
if (FLastCustomOptions=NewCustomOptions) then exit;
FLastCustomOptions:=NewCustomOptions;
OptionsDefTempl:=CodeToolBoss.DefinePool.CreateFPCCommandLineDefines(
'Custom Options', FLastCustomOptions, false, Owner as TProject);
if OptionsDefTempl=nil then begin
// no custom options -> delete old template
if (FSrcDirIf<>nil) and FSrcDirIf.DeleteChild('Custom Options') then
begin
//DebugLn('TProjectDefineTemplates.UpdateDefinesForCustomDefines: Calling CodeToolBoss.DefineTree.ClearCache');
CodeToolBoss.DefineTree.ClearCache;
end;
end else begin
UpdateSrcDirIfDef;
FSrcDirIf.ReplaceChild(OptionsDefTempl);
//DebugLn('TProjectDefineTemplates.UpdateDefinesForCustomDefines: Calling CodeToolBoss.DefineTree.ClearCache');
CodeToolBoss.DefineTree.ClearCache;
end;
end;
procedure TProjectDefineTemplates.FixTemplateOrder;
begin
if (FSrcDirIf<>nil) then
FSrcDirIf.Parent.MoveToLast(FSrcDirIf);
end;
procedure TProjectDefineTemplates.ClearFlags;
begin
FFlags:=FFlags+[ptfCustomDefinesChanged];
end;
procedure TProjectDefineTemplates.AllChanged(AActivating: boolean);
begin
if AActivating then ;
UpdateSrcDirIfDef;
SourceDirectoriesChanged;
CustomDefinesChanged;
UpdateGlobalValues;
end;
procedure TProjectDefineTemplates.UpdateGlobalValues;
var
NewProjectDir: String;
Changed: Boolean;
begin
Changed:=false;
// the LCLWidgetType, TargetCPU and TargetOS is set by the TBuildManager
if (Owner as TProject).IsVirtual then
NewProjectDir:=VirtualDirectory
else
NewProjectDir:=(Owner as TProject).Directory;
if CodeToolBoss.SetGlobalValue(ExternalMacroStart+'ProjPath',NewProjectDir)
then
Changed:=true;
if Changed then
IncreaseCompilerParseStamp;
end;
{ TUnitComponentDependency }
procedure TUnitComponentDependency.SetRequiresUnit(const AValue: TUnitInfo);
begin
if FRequiresUnit=AValue then exit;
if (AValue<>nil) and (FUsedByUnit=AValue) then
raise Exception.Create('TUnitComponentDependency.SetRequiresUnit inconsistency');
if FRequiresUnit<>nil then
RemoveFromList(FRequiresUnit.FFirstUsedByComponent,ucdlUsedBy);
FRequiresUnit:=AValue;
if FRequiresUnit<>nil then
AddToList(FRequiresUnit.FFirstUsedByComponent,ucdlUsedBy);
end;
procedure TUnitComponentDependency.SetTypes(const AValue: TUnitCompDependencyTypes);
begin
if AValue=FTypes then exit;
FTypes:=AValue;
if (not (ucdtOldProperty in FTypes)) and (FCompProps<>nil) then
ClearComponentProperties;
end;
function TUnitComponentDependency.GetCompPropCount: integer;
begin
if FCompProps=nil then
Result:=0
else
Result:=FCompProps.Count;
end;
function TUnitComponentDependency.GetCompProps(Index: integer): TUCDComponentProperty;
begin
Result:=TUCDComponentProperty(FCompProps[Index]);
end;
procedure TUnitComponentDependency.SetUsedByUnit(const AValue: TUnitInfo);
begin
if FUsedByUnit=AValue then exit;
if (AValue<>nil) and (FRequiresUnit=AValue) then
raise Exception.Create('TUnitComponentDependency.SetUsedByUnit inconsistency');
if FUsedByUnit<>nil then
RemoveFromList(FUsedByUnit.FFirstRequiredComponent,ucdlRequires);
FUsedByUnit:=AValue;
if FUsedByUnit<>nil then
AddToList(FUsedByUnit.FFirstRequiredComponent,ucdlRequires);
end;
constructor TUnitComponentDependency.Create;
begin
end;
destructor TUnitComponentDependency.Destroy;
begin
RequiresUnit:=nil;
UsedByUnit:=nil;
ClearComponentProperties;
inherited Destroy;
end;
procedure TUnitComponentDependency.ClearComponentProperties;
var
i: Integer;
begin
if FCompProps=nil then exit;
for i:=0 to FCompProps.Count-1 do TObject(FCompProps[i]).Free;
FreeAndNil(FCompProps);
end;
function TUnitComponentDependency.NextUsedByDependency: TUnitComponentDependency;
begin
Result:=NextDependency[ucdlUsedBy];
end;
function TUnitComponentDependency.PrevUsedByDependency: TUnitComponentDependency;
begin
Result:=PrevDependency[ucdlUsedBy];
end;
function TUnitComponentDependency.NextRequiresDependency: TUnitComponentDependency;
begin
Result:=NextDependency[ucdlRequires];
end;
function TUnitComponentDependency.PrevRequiresDependency: TUnitComponentDependency;
begin
Result:=PrevDependency[ucdlRequires];
end;
procedure TUnitComponentDependency.AddToList(
var FirstDependency: TUnitComponentDependency;
ListType: TUnitCompDependencyList);
begin
NextDependency[ListType]:=FirstDependency;
FirstDependency:=Self;
PrevDependency[ListType]:=nil;
if NextDependency[ListType]<>nil then
NextDependency[ListType].PrevDependency[ListType]:=Self;
end;
procedure TUnitComponentDependency.RemoveFromList(
var FirstDependency: TUnitComponentDependency;
ListType: TUnitCompDependencyList);
begin
if FirstDependency=Self then FirstDependency:=NextDependency[ListType];
if NextDependency[ListType]<>nil then
NextDependency[ListType].PrevDependency[ListType]:=PrevDependency[ListType];
if PrevDependency[ListType]<>nil then
PrevDependency[ListType].NextDependency[ListType]:=NextDependency[ListType];
NextDependency[ListType]:=nil;
PrevDependency[ListType]:=nil;
end;
function TUnitComponentDependency.FindUsedByPropPath(
const UsedByPropPath: string): TUCDComponentProperty;
var
i: Integer;
begin
if FCompProps=nil then exit(nil);
for i:=FCompProps.Count-1 downto 0 do begin
Result:=CompProps[i];
if SysUtils.CompareText(Result.UsedByPropPath,UsedByPropPath)=0 then exit;
end;
Result:=nil;
end;
function TUnitComponentDependency.SetUsedByPropPath(const UsedByPropPath,
RequiresPropPath: string): TUCDComponentProperty;
begin
//DebugLn(['TUnitComponentDependency.SetUsedByPropPath ',UsedByPropPath,'=',RequiresPropPath]);
if (not (ucdtOldProperty in FTypes)) then
raise Exception.Create('TUnitComponentDependency.SetUsedByPropPath inconsistency');
Result:=FindUsedByPropPath(UsedByPropPath);
if Result=nil then begin
if FCompProps=nil then
FCompProps:=TFPList.Create;
Result:=TUCDComponentProperty.Create(UsedByPropPath,RequiresPropPath);
FCompProps.Add(Result);
end else begin
Result.UsedByPropPath:=UsedByPropPath;// update case
Result.RequiresPropPath:=RequiresPropPath;
end;
end;
function TUnitComponentDependency.CreatePropPath(AComponent: TComponent;
const PropName: string): string;
begin
Result:=PropName;
while AComponent<>nil do begin
if Result<>'' then
Result:='.'+Result;
Result:=AComponent.Name+Result;
AComponent:=AComponent.Owner;
end;
end;
{ TUCDComponentProperty }
constructor TUCDComponentProperty.Create(const SrcPath, DestPath: string);
begin
UsedByPropPath:=SrcPath;
RequiresPropPath:=DestPath;
end;
{ TProjectBuildMode }
function TProjectBuildMode.GetLazCompilerOptions: TLazCompilerOptions;
begin
Result:=FCompilerOptions;
end;
constructor TProjectBuildMode.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCompilerOptions:=TProjectCompilerOptions.Create(LazProject);
FCompilerOptions.AddOnChangedHandler(@OnItemChanged);
FCompilerOptions.FBuildMode:=Self;
end;
destructor TProjectBuildMode.Destroy;
begin
FreeAndNil(FCompilerOptions);
inherited Destroy;
end;
function TProjectBuildMode.LazProject: TProject;
begin
if Owner is TProjectBuildModes then
Result:=TProjectBuildModes(Owner).LazProject
else
Result:=Nil;
end;
procedure TProjectBuildMode.Clear;
begin
CompilerOptions.Clear;
end;
function TProjectBuildMode.Equals(Src: TProjectBuildMode): boolean;
begin
Result:=CompilerOptions.IsEqual(Src.CompilerOptions);
end;
function TProjectBuildMode.CreateDiff(Other: TProjectBuildMode;
Tool: TCompilerDiffTool): boolean;
begin
// Note: if there is a Tool all steps must be evaluated, if not exit on first diff
//if Tool<>nil then debugln(['TProjectBuildMode.CreateDiff ']);
Result:=CompilerOptions.CreateDiff(Other.CompilerOptions,Tool);
if (Tool=nil) and Result then exit;
end;
procedure TProjectBuildMode.Assign(Src: TProjectBuildMode);
begin
if Equals(Src) then exit;
InSession:=Src.InSession;
CompilerOptions.Assign(Src.CompilerOptions);
end;
procedure TProjectBuildMode.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
begin
FIdentifier:=XMLConfig.GetValue('Identifier','');
FCompilerOptions.LoadFromXMLConfig(XMLConfig,Path+'CompilerOptions/');
end;
procedure TProjectBuildMode.SaveMacroValuesAtOldPlace(XMLConfig: TXMLConfig; const Path: string);
var
Cnt: Integer;
Modes: TProjectBuildModes;
begin
// for older IDE (<1.1) save the macros at the old place
Assert(Assigned(Owner), 'SaveMacroValuesAtOldPlace: Owner not assigned.');
Modes := Owner as TProjectBuildModes;
Cnt:=Modes.SessionMatrixOptions.SaveAtOldXMLConfig(XMLConfig, Path, Identifier);
Cnt+=Modes.SharedMatrixOptions.SaveAtOldXMLConfig(XMLConfig, Path, Identifier);
XMLConfig.SetDeleteValue(Path+'Count',Cnt,0);
end;
procedure TProjectBuildMode.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; IsDefault, ALegacyList: Boolean; var Cnt: integer);
var
SubPath: String;
begin
SubPath:=Path+'BuildModes/'+XMLConfig.GetListItemXPath('Item', Cnt, ALegacyList, True)+'/';
inc(Cnt);
XMLConfig.SetDeleteValue(SubPath+'Name',Identifier,'');
if IsDefault then
XMLConfig.SetDeleteValue(SubPath+'Default',True,false)
else begin
SaveMacroValuesAtOldPlace(XMLConfig, SubPath+'MacroValues/');
CompilerOptions.SaveToXMLConfig(XMLConfig,SubPath+'CompilerOptions/');
end;
end;
function TProjectBuildMode.GetCaption: string;
var
i: Integer;
begin
Result:=Identifier;
for i:=length(Result) downto 1 do
if Result[i] in ['&',#0..#31,#127] then
System.Delete(Result,i,1);
if Result<>'' then exit;
i:=GetIndex;
if i>=0 then
Result:='['+IntToStr(i)+']';
end;
function TProjectBuildMode.GetIndex: integer;
begin
if LazProject<>nil then
Result:=LazProject.BuildModes.IndexOf(Self)
else
Result:=-1;
end;
{ TProjectBuildModes }
function TProjectBuildModes.GetItems(Index: integer): TProjectBuildMode;
begin
Result:=TProjectBuildMode(fItems[Index]);
end;
function TProjectBuildModes.GetModified: boolean;
begin
Result:=fSavedChangeStamp<>FChangeStamp;
end;
procedure TProjectBuildModes.ItemChanged(Sender: TObject);
begin
{$IFDEF VerboseIDEModified}
debugln(['TProjectBuildModes.OnItemChanged ',DbgSName(Sender)]);
{$ENDIF}
IncreaseChangeStamp;
end;
procedure TProjectBuildModes.SetModified(const AValue: boolean);
var
i: Integer;
begin
if AValue then
IncreaseChangeStamp
else begin
for i:=0 to Count-1 do
Items[i].Modified:=false;
SharedMatrixOptions.Modified:=false;
SessionMatrixOptions.Modified:=false;
fSavedChangeStamp:=FChangeStamp;
end;
end;
constructor TProjectBuildModes.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fChangedHandlers:=TMethodList.Create;
fItems:=TFPList.Create;
FChangeStamp:=LUInvalidChangeStamp;
fSavedChangeStamp:=FChangeStamp;
FSharedMatrixOptions:=TBuildMatrixOptions.Create;
FSharedMatrixOptions.OnChanged:=@ItemChanged;
FSessionMatrixOptions:=TBuildMatrixOptions.Create;
FSessionMatrixOptions.OnChanged:=@ItemChanged;
FManyBuildModes:=TStringList.Create;
end;
destructor TProjectBuildModes.Destroy;
begin
FreeAndNil(fChangedHandlers);
Clear;
FreeAndNil(FManyBuildModes);
FreeAndNil(FSharedMatrixOptions);
FreeAndNil(FSessionMatrixOptions);
FreeAndNil(fItems);
inherited Destroy;
end;
procedure TProjectBuildModes.Clear;
begin
while Count>0 do
Delete(Count-1);
SharedMatrixOptions.Clear;
SessionMatrixOptions.Clear;
//fChangedHandlers.Clear;
end;
function TProjectBuildModes.IsEqual(OtherModes: TProjectBuildModes): boolean;
var
i: Integer;
begin
Result:=true;
if OtherModes.Count<>Count then exit;
for i:=0 to Count-1 do
if not Items[i].Equals(OtherModes[i]) then exit;
if not SharedMatrixOptions.Equals(OtherModes.SharedMatrixOptions) then exit;
if not SessionMatrixOptions.Equals(OtherModes.SessionMatrixOptions) then exit;
Result:=false;
end;
procedure TProjectBuildModes.Assign(Source: TPersistent; WithModified: boolean);
var
OtherModes: TProjectBuildModes;
i: Integer;
CurMode: TProjectBuildMode;
begin
if Source is TProjectBuildModes then begin
FAssigning:=True;
OtherModes:=TProjectBuildModes(Source);
Clear;
for i:=0 to OtherModes.Count-1 do
begin
CurMode:=Add(OtherModes[i].Identifier);
CurMode.Assign(OtherModes[i]);
if WithModified then
CurMode.Modified:=OtherModes[i].Modified;
end;
SharedMatrixOptions.Assign(OtherModes.SharedMatrixOptions);
SessionMatrixOptions.Assign(OtherModes.SessionMatrixOptions);
ManyBuildModes.Assign(OtherModes.ManyBuildModes);
ChangedHandlers.Assign(OtherModes.ChangedHandlers);
if WithModified then
Modified:=OtherModes.Modified;
FAssigning:=False;
end else
inherited Assign(Source);
end;
procedure TProjectBuildModes.Delete(Index: integer);
var
Item: TProjectBuildMode;
begin
Item:=Items[Index];
fItems.Delete(Index);
Item.Free;
{$IFDEF VerboseIDEModified}
debugln(['TProjectBuildModes.Delete ']);
{$ENDIF}
IncreaseChangeStamp;
end;
function TProjectBuildModes.IndexOf(Identifier: string): integer;
begin
Result:=Count-1;
while (Result>=0)
and (SysUtils.CompareText(Identifier,Items[Result].Identifier)<>0) do
dec(Result);
end;
function TProjectBuildModes.IndexOf(aMode: TProjectBuildMode): integer;
begin
Result:=fItems.IndexOf(aMode);
end;
function TProjectBuildModes.Find(Identifier: string): TProjectBuildMode;
var
i: LongInt;
begin
i:=IndexOf(Identifier);
if i>=0 then
Result:=Items[i]
else
Result:=nil;
end;
function TProjectBuildModes.Add(Identifier: string): TProjectBuildMode;
begin
Result:=TProjectBuildMode.Create(Self);
Result.FIdentifier:=Identifier;
if LazProject<>nil then
Result.CompilerOptions.BaseDirectory:=LazProject.Directory;
Result.AddOnChangedHandler(@ItemChanged);
fItems.Add(Result);
end;
procedure TProjectBuildModes.Move(FromIndex, ToIndex: integer);
begin
fItems.Move(FromIndex,ToIndex);
end;
function TProjectBuildModes.Count: integer;
begin
Result:=fItems.Count;
end;
procedure TProjectBuildModes.IncreaseChangeStamp;
begin
LUIncreaseChangeStamp(FChangeStamp);
if fChangedHandlers<>nil then fChangedHandlers.CallNotifyEvents(Self);
end;
procedure TProjectBuildModes.AddOnChangedHandler(const Handler: TNotifyEvent);
begin
fChangedHandlers.Add(TMethod(Handler));
end;
procedure TProjectBuildModes.RemoveOnChangedHandler(const Handler: TNotifyEvent);
begin
fChangedHandlers.Remove(TMethod(Handler));
end;
function TProjectBuildModes.IsModified(InSession: boolean): boolean;
var
i: Integer;
begin
Result:=true;
if InSession then begin
if SessionMatrixOptions.Modified then exit;
end else begin
if SharedMatrixOptions.Modified then exit;
end;
for i:=0 to Count-1 do
if (Items[i].InSession=InSession) and Items[i].Modified then
exit;
Result:=false;
end;
function TProjectBuildModes.GetSessionModes: TStringList;
var
i: Integer;
BuildMode: TProjectBuildMode;
begin
Result:=TStringList.Create;
for i:=0 to Count-1 do begin
BuildMode:=Items[i];
if BuildMode.InSession then
Result.Add(BuildMode.Identifier);
end;
end;
function TProjectBuildModes.IsSessionMode(const ModeIdentifier: string): boolean;
var
i: Integer;
BuildMode: TProjectBuildMode;
begin
for i:=0 to Count-1 do begin
BuildMode:=Items[i];
if SysUtils.CompareText(BuildMode.Identifier,ModeIdentifier)=0 then
exit(BuildMode.InSession);
end;
Result:=false;
end;
function TProjectBuildModes.IsSharedMode(const ModeIdentifier: string): boolean;
var
i: Integer;
BuildMode: TProjectBuildMode;
begin
for i:=0 to Count-1 do begin
BuildMode:=Items[i];
if SysUtils.CompareText(BuildMode.Identifier,ModeIdentifier)=0 then
exit(not BuildMode.InSession);
end;
Result:=false;
end;
procedure TProjectBuildModes.RenameMatrixMode(const OldName, NewName: string);
begin
SharedMatrixOptions.RenameMode(OldName,NewName);
SessionMatrixOptions.RenameMode(OldName,NewName);
end;
function TProjectBuildModes.CreateExtraModes(aCurMode: TProjectBuildMode): TProjectBuildMode;
// Create Debug and Release buildmodes. Return the created debug mode.
// Params: aCurMode - existing mode to copy settings from.
procedure AssignAndSetBooleans(aMode: TProjectBuildMode; IsDebug: Boolean);
begin
if Assigned(aCurMode) then
aMode.Assign(aCurMode); // clone from currently selected mode
with aMode.CompilerOptions do
begin
// Smart linking
SmartLinkUnit:=not IsDebug;
LinkSmart:=not IsDebug;
// Checks
IOChecks:=IsDebug;
RangeChecks:=IsDebug;
OverflowChecks:=IsDebug;
StackChecks:=IsDebug;
IncludeAssertionCode:=IsDebug;
VerifyObjMethodCall:=IsDebug;
// Debug flags
GenerateDebugInfo:=IsDebug;
RunWithoutDebug:=not IsDebug;
UseExternalDbgSyms:=IsDebug;
UseHeaptrc:=IsDebug;
TrashVariables:=IsDebug;
end;
end;
var
RelMode: TProjectBuildMode;
begin
// Create Debug mode
Result:=Add(DebugModeName);
AssignAndSetBooleans(Result, True);
Result.CompilerOptions.OptimizationLevel:=1; // Optimization
Result.CompilerOptions.DebugInfoType:=dsDwarf3; // Debug
// Create Release mode
RelMode:=Add(ReleaseModeName);
AssignAndSetBooleans(RelMode, False);
RelMode.CompilerOptions.OptimizationLevel:=3; // Slow but safe optimization, -O4 is dangerous
RelMode.CompilerOptions.DebugInfoType:=dsAuto; // No Debug
end;
// Methods for LoadFromXMLConfig
procedure TProjectBuildModes.AddMatrixMacro(const MacroName, MacroValue, ModeIdentifier: string;
InSession: boolean);
var
MatrixOptions: TBuildMatrixOptions;
MatrixOption: TBuildMatrixOption;
begin
MatrixOption:=SharedMatrixOptions.FindMacro(MacroName,MacroValue);
if MatrixOption=nil then
MatrixOption:=SessionMatrixOptions.FindMacro(MacroName,MacroValue);
if MatrixOption<>nil then begin
// Macro already exists => enable mode for this macro
MatrixOption.EnableMode(ModeIdentifier);
end else begin
// Macro does not yet exist => create
if InSession then
MatrixOptions:=SessionMatrixOptions
else
MatrixOptions:=SharedMatrixOptions;
MatrixOption:=MatrixOptions.Add(bmotIDEMacro,'*');
MatrixOption.MacroName:=MacroName;
MatrixOption.Value:=MacroValue;
MatrixOption.Modes:=ModeIdentifier;
end;
end;
procedure TProjectBuildModes.LoadSessionEnabledNonSessionMatrixOptions(const Path: string);
var
i, Cnt: integer;
SubPath: String;
ModeID, OptionID: String;
begin
// disable all matrix options in session modes
if FGlobalMatrixOptions<>nil then
FGlobalMatrixOptions.DisableModes(@IsSessionMode);
SharedMatrixOptions.DisableModes(@IsSessionMode);
// load
Cnt:=FXMLConfig.GetValue(Path+'Count',0);
for i:=1 to Cnt do begin
SubPath:=Path+'Item'+IntToStr(i)+'/';
ModeID:=FXMLConfig.GetValue(SubPath+'Mode','');
if (ModeID='') or (not IsSessionMode(ModeID)) then begin
debugln(['LoadSessionEnabledNonSessionMatrixOptions not a session Mode="',dbgstr(ModeID),'" at ',SubPath]);
continue;
end;
OptionID:=FXMLConfig.GetValue(SubPath+'Option','');
if OptionID='' then begin
debugln(['LoadSessionEnabledNonSessionMatrixOptions invalid option at ',SubPath]);
continue;
end;
if Assigned(FGlobalMatrixOptions) then
FGlobalMatrixOptions.EnableModeIfOptionFound(ModeID, OptionID);
if Assigned(SharedMatrixOptions) then
SharedMatrixOptions.EnableModeIfOptionFound(ModeID, OptionID);
end;
end;
procedure TProjectBuildModes.LoadOtherCompilerOpts(const Path: string;
FromIndex, ToIndex: Integer; InSession: boolean);
// Iterate rest of the modes.
var
i: Integer;
Ident, SubPath: String;
CurMode: TProjectBuildMode;
LegacyList: Boolean;
begin
LegacyList := FXMLConfig.IsLegacyList(Path);
for i:=FromIndex to ToIndex do
begin
SubPath:=Path+FXMLConfig.GetListItemXPath('Item', i-1, LegacyList, True)+'/';
Ident:=FXMLConfig.GetValue(SubPath+'Name','');
CurMode:=Add(Ident); // add another mode
CurMode.InSession:=InSession;
CurMode.CompilerOptions.LoadFromXMLConfig(FXMLConfig, SubPath+'CompilerOptions/');
end;
end;
procedure TProjectBuildModes.LoadMacroValues(const Path: string; CurMode: TProjectBuildMode);
var
i, Cnt: Integer;
SubPath, MacroName, MacroValue: String;
begin
// load macro values of old IDE (<1.1)
Cnt:=FXMLConfig.GetValue(Path+'Count',0);
//debugln(['LoadMacroValues Cnt=',Cnt]);
for i:=1 to Cnt do begin
SubPath:=Path+'Macro'+IntToStr(i)+'/';
MacroName:=FXMLConfig.GetValue(SubPath+'Name','');
if not IsValidIdent(MacroName) then continue;
MacroValue:=FXMLConfig.GetValue(SubPath+'Value','');
//debugln(['LoadMacroValues Mode="',CurMode.Identifier,'" ',MacroName,'="',MacroValue,'" session=',CurMode.InSession]);
AddMatrixMacro(MacroName,MacroValue,CurMode.Identifier,CurMode.InSession);
end;
end;
procedure TProjectBuildModes.LoadAllMacroValues(const Path: string; Cnt: Integer);
var
i: Integer;
SubPath: String;
IsLegacyList: Boolean;
begin
// First default mode.
LoadMacroValues(Path+'MacroValues/', Items[0]);
IsLegacyList := FXMLConfig.IsLegacyList(Path+'BuildModes/');
// Iterate rest of the modes.
for i:=2 to Cnt do
begin
SubPath:=Path+'BuildModes/'+FXMLConfig.GetListItemXPath('Item', i-1, IsLegacyList, True);
LoadMacroValues(SubPath+'MacroValues/', Items[i-1]);
end;
end;
procedure TProjectBuildModes.LoadOldFormat(const Path: string);
var
Ident, CompOptsPath, MacroValsPath: String;
CurMode: TProjectBuildMode;
begin
// no build modes => an old file format
CompOptsPath:='CompilerOptions/';
// due to a bug in an old version, the XML path can be 'CompilerOptions/' or ''
if (LazProject.FFileVersion<3)
and (FXMLConfig.GetValue('SearchPaths/CompilerPath/Value','')<>'') then
CompOptsPath:='';
MacroValsPath:=Path+'MacroValues/';
CurMode:=Items[0];
LoadMacroValues(MacroValsPath,CurMode);
if FXMLConfig.GetValue(CompOptsPath+'Version/Value', 0)<10 then begin
// LCLWidgetType was not a macro but a property of its own
Ident := FXMLConfig.GetValue(CompOptsPath+'LCLWidgetType/Value', '');
if (Ident<>'') and (SysUtils.CompareText(Ident,'default')<>0) then
AddMatrixMacro('LCLWidgetType',Ident,'default',false);
end;
CurMode.CompilerOptions.LoadFromXMLConfig(FXMLConfig,CompOptsPath);
end;
procedure TProjectBuildModes.LoadActiveBuildMode(const Path: string);
var
CurMode: TProjectBuildMode;
begin
CurMode:=Find(FXMLConfig.GetValue(Path+'BuildModes/Active','default'));
if CurMode=nil then
CurMode:=Items[0];
LazProject.ActiveBuildMode:=CurMode;
// Many BuildModes selection, a comma separated list.
FManyBuildModes.CommaText:=FXMLConfig.GetValue(Path+'ManyBuildModesSelection/Value','');
end;
procedure TProjectBuildModes.LoadProjOptsFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
// Load for project
var
Cnt: Integer;
IsLegacyList: Boolean;
begin
FXMLConfig := XMLConfig;
IsLegacyList := FXMLConfig.IsLegacyList(Path+'BuildModes/');
Cnt:=FXMLConfig.GetListItemCount(Path+'BuildModes/', 'Item', IsLegacyList);
if Cnt>0 then begin
// Project default mode is stored at the old XML path for backward compatibility.
// Testing the 'Default' XML attribute is not needed because the first mode
// is always default.
Items[0].Identifier:=FXMLConfig.GetValue(Path+'BuildModes/'+XMLConfig.GetListItemXPath('Item', 0, IsLegacyList, True)+'/Name', '');
Items[0].CompilerOptions.LoadFromXMLConfig(FXMLConfig, 'CompilerOptions/');
LoadOtherCompilerOpts(Path+'BuildModes/', 2, Cnt, False);
LoadAllMacroValues(Path+'MacroValues/', Cnt);
end
else
LoadOldFormat(Path);
LoadActiveBuildMode(Path);
end;
procedure TProjectBuildModes.LoadSessionFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string; LoadAllOptions: boolean);
// Load for session
var
Cnt: Integer;
IsLegacyList: Boolean;
begin
FXMLConfig := XMLConfig;
if LoadAllOptions then
// load matrix options
SessionMatrixOptions.LoadFromXMLConfig(FXMLConfig, Path+'BuildModes/SessionMatrixOptions/');
IsLegacyList := FXMLConfig.IsLegacyList(Path+'BuildModes/');
Cnt:=FXMLConfig.GetListItemCount(Path+'BuildModes/', 'Item', IsLegacyList);
if Cnt>0 then begin
// Add a new mode for session compiler options.
LoadOtherCompilerOpts(Path+'BuildModes/', 1, Cnt, True);
LoadAllMacroValues(Path+'MacroValues/', Cnt);
end;
if LoadAllOptions then
// load what matrix options are enabled in session build modes
LoadSessionEnabledNonSessionMatrixOptions(Path+'BuildModes/SessionEnabledMatrixOptions/');
LoadActiveBuildMode(Path);
end;
// Methods for SaveToXMLConfig
procedure TProjectBuildModes.SaveSessionData(const Path: string);
var
SubPath: String;
i, Cnt: Integer;
begin
// Many BuildModes selection, a comma separated list.
FXMLConfig.SetDeleteValue(Path+'ManyBuildModesSelection/Value', FManyBuildModes.CommaText, '');
// save what mode is currently active in the session
FXMLConfig.SetDeleteValue(Path+'BuildModes/Active',
LazProject.ActiveBuildMode.Identifier,'default');
// save matrix options of session
SessionMatrixOptions.SaveToXMLConfig(FXMLConfig, Path+'BuildModes/SessionMatrixOptions/',nil);
// save what matrix options are enabled in session build modes
Cnt:=0;
SubPath:=Path+'BuildModes/SessionEnabledMatrixOptions/';
for i:=0 to Count-1 do
if Items[i].InSession then
SharedMatrixOptions.SaveSessionEnabled(FXMLConfig, SubPath, Items[i].Identifier, Cnt);
if Assigned(FGlobalMatrixOptions) then
for i:=0 to Count-1 do
if Items[i].InSession then
FGlobalMatrixOptions.SaveSessionEnabled(FXMLConfig, SubPath, Items[i].Identifier, Cnt);
FXMLConfig.SetDeleteValue(SubPath+'Count',Cnt,0);
end;
procedure TProjectBuildModes.SaveSharedMatrixOptions(const Path: string);
begin
SharedMatrixOptions.SaveToXMLConfig(FXMLConfig, Path+'BuildModes/SharedMatrixOptions/',@IsSharedMode);
end;
function TProjectBuildModes.GetLazBuildModes(Index: integer): TLazProjectBuildMode;
begin
Result:=TLazProjectBuildMode(fItems[Index]);
end;
// SaveToXMLConfig itself
procedure TProjectBuildModes.SaveProjOptsToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; SaveSession, ALegacyList: boolean);
var
i, Cnt: Integer;
begin
FXMLConfig := XMLConfig;
// Save the default mode under an old xml path to let old IDEs open new projects
// Note: the 0.9.29 reader already supports fetching the default build
// mode from the BuildModes, so in one or two releases we can switch
//Items[0].SaveDefaultCompilerOpts(FXMLConfig, Path);
Items[0].SaveMacroValuesAtOldPlace(XMLConfig,Path+'MacroValues/');
Items[0].CompilerOptions.SaveToXMLConfig(XMLConfig,'CompilerOptions/'); // no Path!
Cnt:=0;
for i:=0 to Count-1 do
if SaveSession or not Items[i].InSession then
Items[i].SaveToXMLConfig(FXMLConfig, Path, i=0, ALegacyList, Cnt);
FXMLConfig.SetListItemCount(Path+'BuildModes/',Cnt,ALegacyList);
end;
procedure TProjectBuildModes.SaveSessionOptsToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; SaveSession, ALegacyList: boolean);
var
i, Cnt: Integer;
begin
FXMLConfig := XMLConfig;
Cnt:=0;
for i:=0 to Count-1 do
if Items[i].InSession and SaveSession then
Items[i].SaveToXMLConfig(FXMLConfig, Path, false, ALegacyList, Cnt);
FXMLConfig.SetListItemCount(Path+'BuildModes/',Cnt,ALegacyList);
end;
initialization
RegisterIDEOptionsGroup(GroupProject, TProjectIDEOptions);
RegisterIDEOptionsGroup(GroupCompiler, TProjectCompilerOptions);
end.