lazarus/ide/project.pp
maxim 45daca322f IDE: Add ability to exclude components from i18n by identifier or original text:
1) Added methods to remove entries from TPOFile by identifier and by original text.
2) Implemented storage of excluded identifiers and originals via TProject.
3) Updated translation routines to pass over project's excluded identifiers and originals when updating PO files.
4) Editing of excluded identifiers and originals in i18n project options.
5) Added option to "Force update PO files on next compile" in i18n project options (auto reset, non-persistent). To force update PO files after changing excluded identifiers and originals.

Patch by Denis Kozlov, bug #29627.

git-svn-id: trunk@51589 -
2016-02-10 22:46:00 +00:00

7263 lines
238 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
}
unit Project;
{$mode objfpc}{$H+}
{$ifdef Trace}
{$ASSERTIONS ON}
{$endif}
interface
{$I ide.inc}
uses
{$IFDEF IDE_MEM_CHECK}
MemCheck,
{$ENDIF}
// RTL + FCL + LCL
Classes, SysUtils, TypInfo, LCLProc, Forms, Controls, Dialogs, maps,
// CodeTools
CodeToolsConfig, ExprEval, DefineTemplates,
BasicCodeTools, CodeToolsCfgScript, CodeToolManager, CodeCache, FileProcs,
// LazUtils
FPCAdds, FileUtil, LazFileUtils, LazFileCache, LazUTF8, Laz2_XMLCfg,
// IDEIntf
PropEdits, CompOptsIntf, ProjectIntf, MacroIntf, MacroDefIntf, UnitResources,
PackageIntf, SrcEditorIntf, IDEOptionsIntf, IDEDialogs, LazIDEIntf,
// SynEdit
SynEdit,
// IDE
CompOptsModes, ProjectResources, LazConf, W32Manifest, ProjectIcon,
LazarusIDEStrConsts, CompilerOptions,
TransferMacros, EditorOptions, IDEProcs, RunParamsOpts, ProjectDefs, ProjPackBase,
FileReferenceList, EditDefineTree, ModeMatrixOpts, 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 = (
uifComponentUsedByDesigner,
uifComponentIndirectlyUsedByDesigner,
uifMarked,
uifInternalFile // data from an internal source (e.g. an editor macro (pascal script) from memory)
);
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: TLazSyntaxHighlighter;
procedure SetFoldState(AValue: String);
procedure SetPageIndex(const AValue: Integer);
procedure SetIsVisibleTab(const AValue: Boolean);
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 FTopLine;
property CursorPos: TPoint read FCursorPos write FCursorPos;
property FoldState: String read FFoldState write SetFoldState;
property IsLocked: Boolean read FIsLocked write FIsLocked;
property CustomHighlighter: Boolean read FCustomHighlighter write FCustomHighlighter; // SetCustomHighlighter
property SyntaxHighlighter: TLazSyntaxHighlighter read FSyntaxHighlighter write FSyntaxHighlighter; // 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
FComponentFallbackClasses: TStrings;
FCustomDefaultHighlighter: boolean;
FDefaultSyntaxHighlighter: TLazSyntaxHighlighter;
FDisableI18NForLFM: boolean;
FEditorInfoList: TUnitEditorInfoList;
FAutoReferenceSourceDir: boolean;
fAutoRevertLockCount: integer;// =0 means, codetools can auto update from disk
fBookmarks: TFileBookmarks;
FBuildFileIfActive: boolean;
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
fFileReadOnly: Boolean;
FFirstRequiredComponent: TUnitComponentDependency;
FFirstUsedByComponent: TUnitComponentDependency;
FFlags: TUnitInfoFlags;
fHasResources: boolean; // source has resource file
FIgnoreFileDateOnDiskValid: boolean;
FIgnoreFileDateOnDisk: longint;
fLoaded: Boolean; // loaded in the source editor, needed to restore open files
fLoadedDesigner: Boolean; // has a visible designer, needed to restore open designers
FLoadingComponent: boolean;
fModified: boolean;
fNext, fPrev: array[TUnitInfoList] of TUnitInfo;
fOnFileBackup: TOnFileBackup;
fOnLoadSaveFilename: TOnLoadSaveFilename;
FOnUnitNameChange: TOnUnitNameChange;
FProject: TProject;
FRevertLockCount: integer;// >0 means IDE is currently reverting this unit
FRunFileIfActive: boolean;
FSessionModified: boolean;
fSource: TCodeBuffer;
fSrcUnitName: String;
fUsageCount: extended;
fUserReadOnly: Boolean;
fSourceChangeStep: LongInt;
FSourceDirectoryReferenced: boolean;
FSourceDirNeedReference: boolean;
fLastDirectoryReferenced: string;
FSetBookmarLock: Integer;
FUnitResourceFileformat: TUnitResourcefileFormatClass;
function GetEditorInfo(Index: Integer): TUnitEditorInfo;
function GetHasResources: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 GetUnitResourceFileformat: TUnitResourcefileFormatClass;
procedure SetAutoReferenceSourceDir(const AValue: boolean);
procedure SetBuildFileIfActive(const AValue: boolean);
procedure SetDefaultSyntaxHighlighter(const AValue: TLazSyntaxHighlighter);
procedure SetDirectives(const AValue: TStrings);
procedure SetDisableI18NForLFM(const AValue: boolean);
procedure SetFileReadOnly(const AValue: Boolean);
procedure SetComponent(const AValue: TComponent);
procedure SetLoaded(const AValue: Boolean);
procedure SetLoadedDesigner(const 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 SetSrcUnitName(const NewUnitName:string);
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 UpdateHasCustomHighlighter(aDefaultHighlighter: TLazSyntaxHighlighter);
procedure UpdatePageIndex;
public
constructor Create(ACodeBuffer: TCodeBuffer);
destructor Destroy; override;
function GetFileOwner: TObject; override;
function GetFileOwnerName: string; override;
function ChangedOnDisk(CompareOnlyLoadSaveTime: 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: 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: TLazSyntaxHighlighter);
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 FBuildFileIfActive
write SetBuildFileIfActive;
property Component: TComponent read fComponent write SetComponent;
property ComponentName: string read fComponentName write fComponentName;
property ComponentResourceName: string read fComponentResourceName
write fComponentResourceName;
property ComponentFallbackClasses: TStrings read FComponentFallbackClasses
write FComponentFallbackClasses; // classname to componentclass, for not registered classes in lfm
property ComponentState: TWindowState read FComponentState write FComponentState;
property ResourceBaseClass: TPFComponentBaseClass read FResourceBaseClass
write FResourceBaseClass;
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 FCustomDefaultHighlighter write FCustomDefaultHighlighter;
property Directives: TStrings read FDirectives write SetDirectives;
property DisableI18NForLFM: boolean read FDisableI18NForLFM write SetDisableI18NForLFM;
property FileReadOnly: Boolean read fFileReadOnly write SetFileReadOnly;
property FirstRequiredComponent: TUnitComponentDependency
read FFirstRequiredComponent;
property FirstUsedByComponent: TUnitComponentDependency
read FFirstUsedByComponent;
property Flags: TUnitInfoFlags read FFlags write FFlags;
property HasResources: boolean read GetHasResources write fHasResources;
property Loaded: Boolean read fLoaded write SetLoaded;
property LoadedDesigner: Boolean read fLoadedDesigner write SetLoadedDesigner;
property LoadingComponent: boolean read FLoadingComponent write FLoadingComponent;
property Modified: boolean read GetModified write SetModified;// not Session data
property SessionModified: boolean read FSessionModified 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 FRunFileIfActive write SetRunFileIfActive;
property Source: TCodeBuffer read fSource write SetSource;
property DefaultSyntaxHighlighter: TLazSyntaxHighlighter
read FDefaultSyntaxHighlighter write SetDefaultSyntaxHighlighter;
property SrcUnitName: String read fSrcUnitName write SetSrcUnitName; // unit name in source
property UserReadOnly: Boolean read fUserReadOnly write SetUserReadOnly;
property SourceDirectoryReferenced: boolean read FSourceDirectoryReferenced;
property AutoReferenceSourceDir: boolean read FAutoReferenceSourceDir
write SetAutoReferenceSourceDir;
end;
//---------------------------------------------------------------------------
{ TProjectCompilationToolOptions }
TProjectCompilationToolOptions = class(TCompilationToolOptions)
private
FCompileReasons: TCompileReasons;
FDefaultCompileReasons: TCompileReasons;
procedure SetCompileReasons(const AValue: TCompileReasons);
procedure SetDefaultCompileReasons(const AValue: TCompileReasons);
protected
procedure SubstituteMacros(var s: string); override;
public
procedure Clear; override;
function CreateDiff(CompOpts: TCompilationToolOptions;
Tool: TCompilerDiffTool): boolean; override;
procedure Assign(Src: TCompilationToolOptions); 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 CompileReasons: TCompileReasons read FCompileReasons write SetCompileReasons;
property DefaultCompileReasons: TCompileReasons read FDefaultCompileReasons write SetDefaultCompileReasons;
end;
TProjectBuildMode = class;
{ TProjectCompilerOptions }
TProjectCompilerOptions = class(TBaseCompilerOptions)
private
FBuildMode: TProjectBuildMode;
FProject: TProject;
FCompileReasons: TCompileReasons;
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 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(const 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;
procedure GetInheritedCompilerOptions(var OptionsList: TFPList); override;
procedure Assign(Source: TPersistent); override;
function IsEqual(CompOpts: TBaseCompilerOptions): boolean; override;
function CreateDiff(CompOpts: TBaseCompilerOptions;
Tool: TCompilerDiffTool = nil): boolean; override; // true if differ
procedure InvalidateOptions;
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;
procedure UpdateSrcDirIfDef; override;
procedure UpdateSourceDirectories; override;
procedure UpdateOutputDirectory; override;
procedure UpdateDefinesForCustomDefines; override;
procedure ClearFlags; override;
public
constructor Create(AOwner: IProjPack);
destructor Destroy; override;
procedure AllChanged; 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: 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;
fSavedChangeStamp: int64;
fItems: TFPList;
FLazProject: TProject;
fOnChanged: TMethodList;
// Variables used by LoadFromXMLConfig and SaveToXMLConfig
FXMLConfig: TXMLConfig;
FGlobalMatrixOptions: TBuildMatrixOptions;
function GetItems(Index: integer): TProjectBuildMode;
function GetModified: boolean;
procedure OnItemChanged(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: boolean);
procedure SaveSessionOptsToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
SaveSession: 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;
end;
{ TProjectIDEOptions }
TProjectIDEOptions = class(TAbstractIDEOptions)
private
FProject: TProject;
public
constructor Create(AProject: TProject);
destructor Destroy; override;
class function GetInstance: TAbstractIDEOptions; override;
class function GetGroupCaption: string; override;
property Project: TProject read FProject;
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;
FChangeStampSaved: integer;
FEnableI18NForLFM: boolean;
FLastCompileComplete: boolean;
FMacroEngine: TTransferMacroList;
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: string;
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: TXMLConfig;
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 JumpHistoryCheckPosition(
APosition:TProjectJumpHistoryPosition): boolean;
function OnUnitFileBackup(const Filename: string): TModalResult;
procedure ClearSourceDirectories;
procedure EmbeddedObjectModified(Sender: TObject);
procedure OnLoadSaveFilename(var AFilename: string; Load: boolean);
procedure OnUnitNameChange(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: string);
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);
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 OnMacroEngineSubstitution({%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 LoadSessionInfo(const Path: string; Merge: boolean);
procedure LoadFromLPI;
procedure LoadFromSession;
function DoLoadLPI(Filename: String): TModalResult;
function DoLoadSession(Filename: String): TModalResult;
// Methods for WriteProject
procedure SaveFlags(const Path: string);
procedure SaveUnits(const Path: string; SaveSession: boolean);
procedure SaveOtherDefines(const Path: string);
procedure SaveSessionInfo(const Path: string);
procedure SaveToLPI;
procedure SaveToSession;
function DoWrite(Filename: String; IsLpi: Boolean): TModalResult;
protected
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;
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 IsReverting(AnUnitInfo: TUnitInfo): boolean;
// load/save
function IsVirtual: boolean; override;
function SomethingModified(CheckData, CheckSession: boolean; Verbose: boolean = false): boolean;
function SomeDataModified(Verbose: boolean = false): boolean;
function SomeSessionModified(Verbose: boolean = false): boolean;
procedure MainSourceFilenameChanged;
procedure GetUnitsChangedOnDisk(var AnUnitList: 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;
procedure UpdateVisibleUnit(AnEditor: TSourceEditorInterface; AWindowID: Integer);
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 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 AllEditorsInfoCount: Integer;
property AllEditorsInfo[Index: Integer]: TUnitEditorInfo read GetAllEditorsInfo;
function EditorInfoWithEditorComponent(AEditor:TSourceEditorInterface): TUnitEditorInfo;
function SearchFile(const ShortFilename: string;
SearchFlags: TSearchIDEFileFlags): TUnitInfo;
function FindFile(const AFilename: string;
SearchFlags: TProjectFileSearchFlags): TLazProjectFile; override;
// Application.CreateForm statements
function AddCreateFormToProjectFile(const AClassName, AName:string):boolean;
function RemoveCreateFormFromProjectFile(const {%H-}AClassName,
AName: string):boolean;
function FormIsCreatedInProjectFile(const AClassname, AName:string):boolean;
// 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;
// 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;
procedure AutoAddOutputDirToIncPath;
// compile state file
function LoadStateFile(IgnoreErrors: boolean): TModalResult;
function SaveStateFile(const CompilerFilename, CompilerParams: string;
Complete: boolean): TModalResult;
// source editor
procedure UpdateAllCustomHighlighter;
procedure UpdateAllSyntaxHighlighter;
// i18n
function GetPOOutDirectory: string;
//auto created forms
function GetAutoCreatedFormsList: TStrings;
property TmpAutoCreatedForms: TStrings read FTmpAutoCreatedForms write FTmpAutoCreatedForms;
// 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;
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 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 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: string 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 POOutputDirectory: string read FPOOutputDirectory write SetPOOutputDirectory;
property ProjectDirectory: string read fProjectDirectory;
property ProjectInfoFile: string read GetProjectInfoFile write SetProjectInfoFile;
property PublishOptions: TPublishProjectOptions read FPublishOptions write FPublishOptions;
property ProjResources: TProjectResources read GetProjResources;
property RunParameterOptions: TRunParamsOptions read GetRunParameterOptions;
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 = nil;// the main project
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 = 9;
ProjOptionsPath = 'ProjectOptions/';
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:='?'
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.SetIsVisibleTab(const AValue: Boolean);
begin
if FIsVisibleTab = AValue then exit;
FIsVisibleTab := 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);
PageIndex := 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);
CursorPos := 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);
FSyntaxHighlighter := StrToLazSyntaxHighlighter(
XMLConfig.GetValue(Path+'SyntaxHighlighter/Value',
LazSyntaxHighlighterNames[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');
XMLConfig.SetDeleteValue(Path+'SyntaxHighlighter/Value',
LazSyntaxHighlighterNames[fSyntaxHighlighter],
LazSyntaxHighlighterNames[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(FComponentFallbackClasses);
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 not fSource.Save then 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 else begin
Result:=mrOk;
FIgnoreFileDateOnDiskValid:=true;
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;
FIgnoreFileDateOnDiskValid:=true;
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 SrcUnitName
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.ReadUnitNameFromSource ',Result]);
{$ENDIF}
fSrcUnitName:=Result;
end else begin
// unable to parse the source
if FilenameIsPascalSource(Filename) then begin
// use default: the filename
Result:=ExtractFileNameOnly(Filename);
if CompareText(Result,fSrcUnitName)=0 then begin
// the last stored unitname has the better case
Result:=SrcUnitName;
end;
end;
end;
end;
function TUnitInfo.GetUsesUnitName: string;
begin
if not FilenameIsPascalUnit(Filename) then
Result:=''
else begin
Result:=SrcUnitName;
if (Result='') or (CompareText(Result,ExtractFileNameOnly(Filename))<>0) then
Result:=ExtractFileNameOnly(Filename);
end;
end;
function TUnitInfo.CreateUnitName: string;
begin
Result:=SrcUnitName;
if (Result='') and FilenameIsPascalSource(Filename) then
Result:=ExtractFilenameOnly(Filename);
end;
{------------------------------------------------------------------------------
TUnitInfo Clear
------------------------------------------------------------------------------}
procedure TUnitInfo.Clear;
begin
FBookmarks.Clear;
FSetBookmarLock := 0;
FBuildFileIfActive:=false;
fComponent := nil;
fComponentName := '';
fComponentResourceName := '';
FComponentState := wsNormal;
FDefaultSyntaxHighlighter := lshText;
FDisableI18NForLFM:=false;
FCustomDefaultHighlighter := False;
FEditorInfoList.ClearEachInfo;
fFilename := '';
fFileReadOnly := false;
fHasResources := false;
FIgnoreFileDateOnDiskValid := false;
fAutoReferenceSourceDir := true;
inherited SetIsPartOfProject(false);
Modified := false;
SessionModified := false;
FRunFileIfActive:=false;
fSrcUnitName := '';
fUsageCount:=-1;
fUserReadOnly := 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;
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;
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',FDisableI18NForLFM,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',fHasResources,false);
XMLConfig.SetDeleteValue(Path+'ResourceBaseClass/Value',
PFComponentBaseClassNames[FResourceBaseClass],
PFComponentBaseClassNames[pfcbcNone]);
s:=fSrcUnitName;
if (s<>'') and (ExtractFileNameOnly(Filename)=s) then s:=''; // only save if SrcUnitName differ from filename
XMLConfig.SetDeleteValue(Path+'UnitName/Value',s,'');
// save custom data
SaveStringToStringTree(XMLConfig,CustomData,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
if (Project.Bookmarks.BookmarkWithID(Bookmarks[i].ID) = nil) or
(Project.Bookmarks.BookmarkWithID(Bookmarks[i].ID).UnitInfo <> self)
then
Bookmarks.Delete(i)
else
if TSynEdit(OpenEditorInfo[0].EditorComponent.EditorControl).GetBookMark(Bookmarks[i].ID, X{%H-}, Y{%H-})
then
Bookmarks[i].CursorPos := Point(X, Y);
end;
FBookmarks.SaveToXMLConfig(XMLConfig,Path+'Bookmarks/');
XMLConfig.SetDeleteValue(Path+'Loaded/Value',fLoaded,false);
XMLConfig.SetDeleteValue(Path+'LoadedDesigner/Value',fLoadedDesigner,false);
XMLConfig.SetDeleteValue(Path+'ReadOnly/Value',fUserReadOnly,false);
XMLConfig.SetDeleteValue(Path+'BuildFileIfActive/Value',
FBuildFileIfActive,false);
XMLConfig.SetDeleteValue(Path+'RunFileIfActive/Value',
FRunFileIfActive,false);
// save custom session data
SaveStringToStringTree(XMLConfig,CustomSessionData,Path+'CustomSessionData/');
XMLConfig.SetDeleteValue(Path+'DefaultSyntaxHighlighter/Value',
LazSyntaxHighlighterNames[FDefaultSyntaxHighlighter],
LazSyntaxHighlighterNames[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));
FDisableI18NForLFM:=XMLConfig.GetValue(Path+'DisableI18NForLFM/Value',false);
HasResources:=XMLConfig.GetValue(Path+'HasResources/Value',false);
FResourceBaseClass:=StrToComponentBaseClass(
XMLConfig.GetValue(Path+'ResourceBaseClass/Value',''));
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
fSrcUnitName:=XMLConfig.GetValue(Path+'UnitName/Value','');
if fSrcUnitName='' then
fSrcUnitName:=ExtractFileNameOnly(Filename);
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.LoadFromXMLConfig ',fSrcUnitName]);
{$ENDIF}
end else
fSrcUnitName:='';
// save custom data
LoadStringToStringTree(XMLConfig,CustomData,Path+'CustomData/');
end;
// session data
FDefaultSyntaxHighlighter := StrToLazSyntaxHighlighter(
XMLConfig.GetValue(Path+'DefaultSyntaxHighlighter/Value',
LazSyntaxHighlighterNames[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)+'/');
Loaded:=XMLConfig.GetValue(Path+'Loaded/Value',false);
if Loaded then
LoadedDesigner:=XMLConfig.GetValue(Path+'LoadedDesigner/Value',FileVersion<8)
else
LoadedDesigner:=false;
fUserReadOnly:=XMLConfig.GetValue(Path+'ReadOnly/Value',false);
FBuildFileIfActive:=XMLConfig.GetValue(Path+'BuildFileIfActive/Value',
false);
FRunFileIfActive:=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.SetSrcUnitName(const NewUnitName:string);
var
Allowed: boolean;
OldUnitName: String;
begin
if (fSrcUnitName <> NewUnitName) and (NewUnitName <> '') then
begin
Allowed := true;
OldUnitName := fSrcUnitName;
if OldUnitName = '' then
OldUnitName := ExtractFileNameOnly(Filename);
if Assigned(FOnUnitNameChange) then
FOnUnitNameChange(Self, OldUnitName, NewUnitName, false, Allowed);
// (ignore Allowed)
if (fSource <> nil) then
begin
CodeToolBoss.RenameSource(fSource,NewUnitName);
end;
{$IFDEF VerboseIDESrcUnitName}
if CompareFilenames(ExtractFileNameOnly(Filename),'interpkgconflictfiles')=0 then
debugln(['TUnitInfo.SetSrcUnitName ',NewUnitName]);
{$ENDIF}
fSrcUnitName := NewUnitName;
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 SourceDirectoryReferenced
and (Project<>nil)
and (fLastDirectoryReferenced<>GetDirectory) then begin
Project.SourceDirectories.RemoveFilename(fLastDirectoryReferenced);
FSourceDirectoryReferenced:=false;
end;
fFileName:=NewFilename;
if EditorOpts<>nil then
UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(FFilename));
UpdateSourceDirectoryReference;
end;
procedure TUnitInfo.UpdateHasCustomHighlighter(aDefaultHighlighter: TLazSyntaxHighlighter);
var
i: Integer;
begin
FCustomDefaultHighlighter := FDefaultSyntaxHighlighter <> aDefaultHighlighter;
for i := 0 to FEditorInfoList.Count - 1 do
FEditorInfoList[i].CustomHighlighter :=
FEditorInfoList[i].SyntaxHighlighter <> aDefaultHighlighter;
end;
procedure TUnitInfo.UpdatePageIndex;
var
HasPageIndex: Boolean;
BookmarkID, i, j: integer;
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
BookmarkID := Bookmarks[i].ID;
j := Project1.Bookmarks.IndexOfID(BookmarkID);
if (j < 0) then
TSynEdit(OpenEditorInfo[0].EditorComponent.EditorControl).SetBookMark(BookmarkID,
Bookmarks[i].CursorPos.X, Bookmarks[i].CursorPos.Y);
end;
finally
dec(FSetBookmarLock);
end;
end
else // OpenEditorInfoCount = 0
Project1.Bookmarks.DeleteAllWithUnitInfo(Self);
end;
end;
procedure TUnitInfo.UpdateDefaultHighlighter(aDefaultHighlighter: TLazSyntaxHighlighter);
var
i: Integer;
begin
//debugln(['TUnitInfo.UpdateDefaultHighlighter ',Filename,' ',ord(aDefaultHighlighter)]);
if not FCustomDefaultHighlighter 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
RaiseException('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.ProjectDirectory
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.ChangedOnDisk(CompareOnlyLoadSaveTime: boolean): boolean;
begin
Result:=(Source<>nil) and Source.FileOnDiskHasChanged;
//if Result then debugln(['TUnitInfo.ChangedOnDisk ',Filename,' FileAgeCached=',FileAgeCached(Source.Filename)]);
if Result
and (not CompareOnlyLoadSaveTime)
and FIgnoreFileDateOnDiskValid
and (FIgnoreFileDateOnDisk=Source.FileDateOnDisk) then
Result:=false;
if (not IsVirtual) and FileExistsCached(Filename) then
FileReadOnly:=not FileIsWritableCached(Filename)
else
FileReadOnly:=false;
end;
procedure TUnitInfo.IgnoreCurrentFileDateOnDisk;
begin
if Source<>nil then begin
FIgnoreFileDateOnDiskValid:=true;
FIgnoreFileDateOnDisk:=Source.FileDateOnDisk;
end
end;
function TUnitInfo.ShortFilename: string;
begin
if Project<>nil then begin
Result:=Project.RemoveProjectPathFromFilename(Filename);
end else begin
Result:=Filename;
end;
end;
function TUnitInfo.NeedsSaveToDisk: boolean;
begin
Result:=IsVirtual or Modified or ChangedOnDisk(true);
//DebugLn(['TUnitInfo.NeedsSaveToDisk ',filename,' Result=',Result,' Modified=',Modified]);
if not Result then begin
if Source<>nil then
Result:=Source.FileOnDiskNeedsUpdate
else
Result:=not FileExistsUTF8(Filename);
end;
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
FSourceDirNeedReference:=IsPartOfProject and (FilenameIsPascalSource(Filename));
if (not AutoReferenceSourceDir) or (FProject=nil) then exit;
if FSourceDirNeedReference then begin
if not SourceDirectoryReferenced 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 SourceDirectoryReferenced 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
fSourceChangeStep:=FSource.ChangeStep;
exit;
end;
if (fSource<>nil) and IsAutoRevertLocked then
fSource.UnlockAutoDiskRevert;
fSource:=ABuffer;
FIgnoreFileDateOnDiskValid:=false;
if (fSource<>nil) then begin
fSourceChangeStep:=FSource.ChangeStep;
if IsAutoRevertLocked then
fSource.LockAutoDiskRevert;
SetInternalFilename(fSource.FileName);
if (fProject<>nil) and (fProject.MainUnitInfo=Self) then
fProject.MainSourceFilenameChanged;
end;
end;
procedure TUnitInfo.SetUserReadOnly(const NewValue: boolean);
begin
fUserReadOnly:=NewValue;
if fSource<>nil then
fSource.ReadOnly:=ReadOnly;
end;
function TUnitInfo.GetHasResources:boolean;
begin
Result:=fHasResources or (ComponentName<>'');
end;
function TUnitInfo.GetEditorInfo(Index: Integer): TUnitEditorInfo;
begin
Result := FEditorInfoList[Index];
end;
function TUnitInfo.GetModified: boolean;
begin
Result:=fModified
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.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;
procedure TUnitInfo.SetAutoReferenceSourceDir(const AValue: boolean);
begin
if FAutoReferenceSourceDir=AValue then exit;
FAutoReferenceSourceDir:=AValue;
UpdateSourceDirectoryReference;
end;
procedure TUnitInfo.SetBuildFileIfActive(const AValue: boolean);
begin
if FBuildFileIfActive=AValue then exit;
FBuildFileIfActive:=AValue;
SessionModified:=true;
end;
procedure TUnitInfo.SetDefaultSyntaxHighlighter(const AValue: TLazSyntaxHighlighter);
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.SetDirectives(const AValue: TStrings);
begin
if FDirectives=AValue then exit;
FDirectives:=AValue;
end;
procedure TUnitInfo.SetDisableI18NForLFM(const AValue: boolean);
begin
if FDisableI18NForLFM=AValue then exit;
FDisableI18NForLFM:=AValue;
Modified:=true;
end;
procedure TUnitInfo.SetFileReadOnly(const AValue: Boolean);
begin
if fFileReadOnly=AValue then exit;
fFileReadOnly:=AValue;
if fSource<>nil then
fSource.ReadOnly:=ReadOnly;
SessionModified:=true;
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.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 fLoaded=AValue then exit;
fLoaded:=AValue;
if fLoaded then begin
IncreaseAutoRevertLock;
UpdateUsageCount(uuIsLoaded,0);
end else begin
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 fLoadedDesigner=AValue then exit;
fLoadedDesigner:=AValue;
end;
procedure TUnitInfo.SetModified(const AValue: boolean);
begin
if Modified=AValue then exit;
{$IFDEF VerboseIDEModified}
debugln(['TUnitInfo.SetModified ',Filename,' new Modified=',AValue]);
{$ENDIF}
fModified:=AValue;
if (not fModified) and Assigned(Source) then
fSourceChangeStep:=Source.ChangeStep;
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 FRunFileIfActive=AValue then exit;
FRunFileIfActive:=AValue;
SessionModified:=true;
end;
procedure TUnitInfo.SetSessionModified(const AValue: boolean);
begin
if FSessionModified=AValue then exit;
{$IFDEF VerboseIDEModified}
debugln(['TUnitInfo.SetSessionModified ',Filename,' new Modified=',AValue]);
{$ENDIF}
FSessionModified:=AValue;
end;
{ TProjectIDEOptions }
constructor TProjectIDEOptions.Create(AProject: TProject);
begin
inherited Create;
FProject := AProject;
end;
destructor TProjectIDEOptions.Destroy;
begin
inherited Destroy;
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:=@OnMacroEngineSubstitution;
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:=@OnLoadSaveFilename;
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;
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);
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(pfRunnable, OldProjectType in [ptProgram,ptApplication,ptCustomProgram]);
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.LoadSessionInfo(const Path: string; Merge: boolean);
var
NewUnitInfo: TUnitInfo;
NewUnitCount, i: integer;
SubPath: String;
NewUnitFilename: String;
OldUnitInfo: TUnitInfo;
MergeUnitInfo: Boolean;
begin
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject D reading units');{$ENDIF}
NewUnitCount:=FXMLConfig.GetValue(Path+'Units/Count',0);
for i := 0 to NewUnitCount - 1 do begin
SubPath:=Path+'Units/Unit'+IntToStr(i)+'/';
NewUnitFilename:=FXMLConfig.GetValue(SubPath+'Filename/Value','');
OnLoadSaveFilename(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.GetValue(Path+'Units/Count',0)=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', '');
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
LoadStringToStringTree(FXMLConfig,CustomData,Path+'CustomData/');
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject update ct boss');{$ENDIF}
CodeToolBoss.GlobalValues.Variables[ExternalMacroStart+'ProjPath']:=ProjectDirectory;
CodeToolBoss.DefineTree.ClearCache;
// load the dependencies
LoadPkgDependencyList(FXMLConfig,Path+'RequiredPackages/',
FFirstRequiredDependency,pdlRequires,Self,true,false);
// load the Run and Build parameter Options
RunParameterOptions.Load(FXMLConfig,Path,fPathDelimChanged);
// load the Publish Options
PublishOptions.LoadFromXMLConfig(FXMLConfig,Path+'PublishOptions/',fPathDelimChanged);
// load defines used for custom options
LoadOtherDefines(Path);
// load session info
LoadSessionInfo(Path,false);
// 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);
// 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:=CTInvalidChangeStamp;
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);
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;
// Method ReadProject itself
function TProject.ReadProject(const NewProjectInfoFile: string;
GlobalMatrixOptions: TBuildMatrixOptions; LoadAllOptions: Boolean): TModalResult;
begin
Result := mrCancel;
BeginUpdate(true);
try
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;
finally
EndUpdate;
FAllEditorsInfoList.SortByPageIndex;
end;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TProject.ReadProject END');{$ENDIF}
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/Unit'+IntToStr(SaveUnitCount)+'/',True,SaveSession,fCurStorePathDelim);
inc(SaveUnitCount);
end;
FXMLConfig.SetDeleteValue(Path+'Units/Count',SaveUnitCount,0);
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.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);
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
// 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.SetValue(Path+'General/MainUnit/Value', MainUnitID); // always write a value to support opening by older IDEs (<=0.9.28). This can be changed in a few releases.
FXMLConfig.SetDeleteValue(Path+'General/AutoCreateForms/Value',
AutoCreateForms,true);
FXMLConfig.SetDeleteValue(Path+'General/Title/Value', Title,'');
FXMLConfig.SetDeleteValue(Path+'General/UseAppBundle/Value', UseAppBundle, True);
// fpdoc
FXMLConfig.SetDeleteValue(Path+'LazDoc/Paths',
SwitchPathDelims(CreateRelativeSearchPath(FPDocPaths,ProjectDirectory),
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,ProjectDirectory),
fCurStorePathDelim), '');
SaveStringList(FXMLConfig, FI18NExcludedIdentifiers, Path+'i18n/ExcludedIdentifiers/');
SaveStringList(FXMLConfig, FI18NExcludedOriginals, Path+'i18n/ExcludedOriginals/');
// Resources
ProjResources.WriteToProjectFile(FXMLConfig, Path);
// save custom data
SaveStringToStringTree(FXMLConfig,CustomData,Path+'CustomData/');
// Save the macro values and compiler options
BuildModes.SaveProjOptsToXMLConfig(FXMLConfig, Path, FSaveSessionInLPI);
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
RunParameterOptions.Save(FXMLConfig,Path,fCurStorePathDelim);
// save dependencies
SavePkgDependencyList(FXMLConfig,Path+'RequiredPackages/',
FFirstRequiredDependency,pdlRequires,fCurStorePathDelim);
// save units
SaveUnits(Path,FSaveSessionInLPI);
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];
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
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);
BuildModes.SaveSessionData(Path);
// save all units
SaveUnits(Path,true);
// save defines used for custom options
SaveOtherDefines(Path);
// save session info
SaveSessionInfo(Path);
// 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;
// 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;
if not (WriteLPI or WriteLPS) then exit(mrOk);
end;
//debugln(['TProject.WriteProject WriteLPI=',WriteLPI,' WriteLPS=',WriteLPS,' Modifed=',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:=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:=@OnUnitFileBackup;
AnUnit.OnLoadSaveFilename:=@OnLoadSaveFilename;
AnUnit.OnUnitNameChange:=@OnUnitNameChange;
// lock the main unit (when it is changed on disk it should *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,'',true);
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.SrcUnitName<>'') then begin
CodeToolBoss.RemoveUnitFromAllUsesSections(MainUnitInfo.Source,
OldUnitInfo.SrcUnitName);
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 EditorOpts<>nil then
AnUnitInfo.DefaultSyntaxHighlighter := FilenameToLazSyntaxHighlighter(NewBuf.Filename);
Result:=AnUnitInfo;
end;
procedure TProject.UpdateVisibleUnit(AnEditor: TSourceEditorInterface; AWindowID: Integer);
var
i: Integer;
begin
for i := 0 to AllEditorsInfoCount - 1 do
if AllEditorsInfo[i].WindowID = AWindowID then
AllEditorsInfo[i].IsVisibleTab := AllEditorsInfo[i].EditorComponent = 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 := '';
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 RaiseException('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
if AValue=SessionModified then exit;
{$IFDEF VerboseIDEModified}
debugln(['TProject.SetSessionModified new Modified=',AValue]);
{$ENDIF}
inherited SetSessionModified(AValue);
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 TProject.NewUniqueUnitName(const AnUnitName: string):string;
function ExpandedUnitname(const AnUnitName:string):string;
begin
Result:=uppercase(ExtractFileNameOnly(AnUnitName));
end;
function UnitNameExists(const AnUnitName:string):boolean;
var i:integer;
ExpName:string;
begin
Result:=true;
ExpName:=ExpandedUnitName(AnUnitName);
if ExtractFileNameOnly(fProjectInfoFile)=ExpName then exit;
for i:=0 to UnitCount-1 do
if (Units[i].IsPartOfProject)
and (ExpandedUnitName(Units[i].FileName)=ExpName) then
exit;
Result:=false;
end;
var
u:integer;
Prefix: string;
begin
Prefix:=AnUnitName;
while (Prefix<>'') and (Prefix[length(Prefix)] in ['0'..'9']) do
Prefix:=copy(Prefix,1,length(Prefix)-1);
if (Prefix='') or (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
FileNameOnly: String;
FileExt: String;
i: Integer;
begin
FileNameOnly:=ExtractFilenameOnly(Filename);
while (FileNameOnly<>'')
and (FileNameOnly[length(FileNameOnly)] in ['0'..'9']) do
FileNameOnly:=copy(FileNameOnly,1,length(FileNameOnly)-1);
FileExt:=ExtractFileExt(Filename);
i:=0;
repeat
inc(i);
Result:=FileNameOnly+IntToStr(i)+FileExt;
until ProjectUnitWithShortFilename(Result)=nil;
end;
function TProject.AddCreateFormToProjectFile(const AClassName, AName: string):boolean;
begin
if (pfMainUnitHasCreateFormStatements in Project1.Flags) then begin
Result:=CodeToolBoss.AddCreateFormStatement(MainUnitInfo.Source,
AClassName,AName);
if Result then begin
MainUnitInfo.Modified:=true;
end;
end else begin
Result:=false;
end;
end;
function TProject.RemoveCreateFormFromProjectFile(const AClassName,AName:string):boolean;
begin
Result:=CodeToolBoss.RemoveCreateFormStatement(MainUnitInfo.Source,AName);
if Result then begin
MainUnitInfo.Modified:=true;
end;
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].SrcUnitName<>'')
then begin
if (CompareDottedIdentifiers(PChar(Units[Result].SrcUnitName),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(ProjectDirectory);
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
Result:=copy(Result,length(CurPath)+1,length(Result));
end;
end;
procedure TProject.ConvertToLPIFilename(var AFilename: string);
begin
OnLoadSaveFilename(AFilename,false);
end;
procedure TProject.ConvertFromLPIFilename(var AFilename: string);
begin
OnLoadSaveFilename(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.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.OnUnitFileBackup(const Filename: string): TModalResult;
begin
if Assigned(fOnFileBackup) then
Result:=fOnFileBackup(Filename)
else
Result:=mrOk;
end;
procedure TProject.OnLoadSaveFilename(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(ProjectDirectory);
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 begin
AFilename:=CreateRelativePath(AFilename,ProjectPath);
end;
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:=ProjectDirectory;
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 (not IsVirtual) then begin
ProjectDir:=ProjectDirectory;
FilePath:=LeftStr(AFilename,length(ProjectDir));
Result:=(CompareFileNames(ProjectDir,FilePath)=0);
end else
Result:=false;
end else
Result:=true;
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(UnitPathMacroName,CompilerOptions.OtherUnitFiles);
ExtendPath(IncludePathMacroName,CompilerOptions.IncludePath);
ExtendPath(SrcPathMacroName,CompilerOptions.SrcPath);
end;
procedure TProject.GetUnitsChangedOnDisk(var AnUnitList: TFPList);
var
AnUnitInfo: TUnitInfo;
begin
AnUnitList:=nil;
AnUnitInfo:=fFirst[uilAutoRevertLocked];
while (AnUnitInfo<>nil) do begin
if (AnUnitInfo.Source<>nil)
and AnUnitInfo.ChangedOnDisk(false) then begin
if AnUnitList=nil then
AnUnitList:=TFPList.Create;
AnUnitList.Add(AnUnitInfo);
end;
AnUnitInfo:=AnUnitInfo.fNext[uilAutoRevertLocked];
end;
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,pdlRequires,
PackageName);
end;
function TProject.FindRemovedDependencyByName(const PkgName: string): TPkgDependency;
begin
Result:=FindDependencyByNameInList(FFirstRemovedDependency,pdlRequires,PkgName);
end;
function TProject.RequiredDepByIndex(Index: integer): TPkgDependency;
begin
Result:=GetDependencyWithIndex(FFirstRequiredDependency,pdlRequires,Index);
end;
function TProject.RemovedDepByIndex(Index: integer): TPkgDependency;
begin
Result:=GetDependencyWithIndex(FFirstRemovedDependency,pdlRequires,Index);
end;
procedure TProject.AddRequiredDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.AddToList(FFirstRequiredDependency,pdlRequires);
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,pdlRequires);
Dependency.RequiredPackage:=nil;
Dependency.AddToList(FFirstRemovedDependency,pdlRequires);
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,pdlRequires);
Dependency.Free;
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
EndUpdate;
end;
procedure TProject.DeleteRemovedDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RequiredPackage:=nil;
Dependency.RemoveFromList(FFirstRemovedDependency,pdlRequires);
Dependency.Free;
EndUpdate;
end;
procedure TProject.RemoveRemovedDependency(Dependency: TPkgDependency);
begin
BeginUpdate(true);
Dependency.RemoveFromList(FFirstRemovedDependency,pdlRequires);
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,pdlRequires);
FDefineTemplates.CustomDefinesChanged;
IncreaseCompilerParseStamp;
EndUpdate;
end;
procedure TProject.MoveRequiredDependencyDown(Dependency: TPkgDependency);
begin
if Dependency.NextRequiresDependency=nil then exit;
BeginUpdate(true);
Dependency.MoveDownInList(FFirstRequiredDependency,pdlRequires);
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,pdlRequires,
APackage)<>nil;
end;
procedure TProject.GetAllRequiredPackages(var List: TFPList;
ReqFlags: TPkgIntfRequiredFlags; MinPolicy: TPackageUpdatePolicy);
begin
if Assigned(OnGetAllRequiredPackages) then
OnGetAllRequiredPackages(nil,FirstRequiredDependency,List,ReqFlags,MinPolicy);
end;
procedure TProject.AddPackageDependency(const PackageName: string);
var
PkgDependency: TPkgDependency;
begin
if FindDependencyByNameInList(FirstRequiredDependency,pdlRequires,PackageName)
<>nil then exit;
PkgDependency:=TPkgDependency.Create;
PkgDependency.PackageName:=PackageName;
AddRequiredDependency(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;
ReferenceComponent: TComponent;
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
ReferenceComponent:=TComponent(GetObjectProp(AComponent,PropInfo));
//debugln('TProject.UpdateUnitComponentDependencies Property ',dbgsName(AComponent),' Name=',PropInfo^.Name,' Type=',PropInfo^.PropType^.Name,' Value=',dbgsName(ReferenceComponent),' TypeInfo=',TypeInfo^.Name);
if ReferenceComponent is TComponent then begin
// reference is a TComponent
OwnerComponent:=ReferenceComponent;
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(ReferenceComponent));
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
Exclude(AnUnitInfo.FFlags,uifMarked);
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;
ReferenceComponent: TComponent;
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
ReferenceComponent:=TComponent(GetObjectProp(AComponent,PropInfo));
//debugln('TProject.FindUnitsUsingSubComponent Property ',dbgsName(AComponent),' Name=',PropInfo^.Name,' Type=',PropInfo^.PropType^.Name,' Value=',dbgsName(ReferenceComponent),' TypeInfo=',TypeInfo^.Name);
if ReferenceComponent=SubComponent then begin
if List.IndexOf(AnUnitInfo)<0 then
List.Add(AnUnitInfo);
end;
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.SrcPath:=MergeSearchPaths(CompilerOptions.SrcPath,
GetForcedPathDelims(SrcPathAddition));
end;
function TProject.GetSourceDirs(WithProjectDir, WithoutOutputDir: boolean): string;
begin
Result:=SourceDirectories.CreateSearchPathFromAllFiles;
if WithProjectDir then
Result:=MergeSearchPaths(Result,ProjectDirectory);
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:=ProjectDirectory;
Result:=AppendPathDelim(Result)+ChangeFileExt(GetCompileSourceFilename,'.compiled');
end;
function TProject.GetCompileSourceFilename: string;
begin
if MainUnitID<0 then
Result:=''
else
Result:=ExtractFilename(MainUnitInfo.Filename);
end;
procedure TProject.AutoAddOutputDirToIncPath;
var
IncPath: String;
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
IncPath:=CompilerOptions.IncludePath;
if SearchDirectoryInSearchPath(IncPath,'$(ProjOutDir)')<1 then
CompilerOptions.IncludePath:=MergeSearchPaths(IncPath,';$(ProjOutDir)');
end;
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
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:=XMLConfig.GetValue('Params/Value','');
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, CompilerParams: string;
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',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,[mbAbort,mbCancel]);
exit;
end;
end;
Result:=mrOk;
end;
procedure TProject.UpdateAllCustomHighlighter;
var
i: Integer;
begin
if EditorOpts=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 EditorOpts=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(ProjectDirectory)+Result);
end;
function TProject.GetAutoCreatedFormsList: TStrings;
var
i, j: integer;
begin
if (MainUnitID >= 0) then
begin
Result := CodeToolBoss.ListAllCreateFormStatements(MainUnitInfo.Source);
if Result <> nil then
for i := 0 to Result.Count - 1 do
begin
j := Pos(':', Result[i]);
if j > 0 then
if 't' + LowerCase(Copy(Result[i], 1, j - 1)) = LowerCase(
Copy(Result[i], j + 1, Length(Result[i]) - j)) then
Result[i] := Copy(Result[i], 1, j - 1);
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.OnUnitNameChange(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].SrcUnitName<>'')
and (CompareText(Units[i].SrcUnitName,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
if FActiveBuildMode=AValue then exit;
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: string);
begin
if FLastCompilerParams=AValue then Exit;
//debugln(['TProject.SetLastCompilerParams Old="',FLastCompilerParams,'"']);
//debugln(['TProject.SetLastCompilerParams New="',AValue,'"']);
FLastCompilerParams:=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 (SysUtils.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.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.OnMacroEngineSubstitution(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 (MacroName<>'') and 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
Result:=CompareFilenamesIgnoreCase(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.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.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.SrcUnitName)=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:=CTInvalidChangeStamp;
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]<>[])
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 (FilenameIsPascalUnit(AnUnitInfo.Filename)
and (not FilenameIsPascalUnit(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 }
procedure TProjectCompilationToolOptions.SetCompileReasons(
const AValue: TCompileReasons);
begin
if FCompileReasons=AValue then exit;
FCompileReasons:=AValue;
{$IFDEF VerboseIDEModified}
debugln(['TProjectCompilationToolOptions.SetCompileReasons']);
{$ENDIF}
IncreaseChangeStamp;
end;
procedure TProjectCompilationToolOptions.SetDefaultCompileReasons(
const AValue: TCompileReasons);
begin
if FDefaultCompileReasons=AValue then exit;
FDefaultCompileReasons:=AValue;
{$IFDEF VerboseIDEModified}
debugln(['TProjectCompilationToolOptions.SetDefaultCompileReasons']);
{$ENDIF}
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;
procedure TProjectCompilationToolOptions.Clear;
begin
inherited Clear;
CompileReasons := crAll;
end;
function TProjectCompilationToolOptions.CreateDiff(
CompOpts: TCompilationToolOptions; Tool: TCompilerDiffTool): boolean;
begin
if (CompOpts is TProjectCompilationToolOptions) then begin
Result:=AddCompileReasonsDiff('CompileReasons',CompileReasons,
TProjectCompilationToolOptions(CompOpts).CompileReasons,Tool);
end else begin
Result:=true;
if Tool<>nil then Tool.Differ:=true;
end;
if (Tool=nil) and Result then exit;
if (inherited CreateDiff(CompOpts, Tool)) then Result:=true;
end;
procedure TProjectCompilationToolOptions.Assign(Src: TCompilationToolOptions);
begin
inherited Assign(Src);
if Src is TProjectCompilationToolOptions
then begin
CompileReasons := TProjectCompilationToolOptions(Src).CompileReasons;
end
else begin
CompileReasons := crAll;
end;
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;
{ TProjectCompilerOptions }
procedure TProjectCompilerOptions.LoadFromXMLConfig(AXMLConfig: TXMLConfig;
const Path: string);
begin
inherited LoadFromXMLConfig(AXMLConfig,Path);
//FileVersion:=aXMLConfig.GetValue(Path+'Version/Value', 0);
// old compatibility
if AXMLConfig.GetValue(Path+'SkipCompiler/Value',false) then
FCompileReasons := []
else
FCompileReasons := LoadXMLCompileReasons(AXMLConfig,Path+'CompileReasons/',crAll);
//debugln(['TProjectCompilerOptions.LoadFromXMLConfig ',Path+'CompileReasons/ ',crCompile in FCompileReasons]);
end;
procedure TProjectCompilerOptions.SaveToXMLConfig(AXMLConfig: TXMLConfig;
const Path: string);
begin
inherited SaveToXMLConfig(AXMLConfig,Path);
SaveXMLCompileReasons(AXMLConfig, Path+'CompileReasons/', FCompileReasons, crAll);
//debugln(['TProjectCompilerOptions.SaveToXMLConfig ',Path+'CompileReasons/ ',crCompile in FCompileReasons]);
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;
InvalidateOptions;
inherited SetCustomOptions(AValue);
if IsActive then
LazProject.DefineTemplates.CustomDefinesChanged;
end;
procedure TProjectCompilerOptions.SetIncludePaths(const AValue: string);
begin
if IncludePath=AValue then exit;
InvalidateOptions;
inherited SetIncludePaths(AValue);
end;
procedure TProjectCompilerOptions.SetLibraryPaths(const AValue: string);
begin
if Libraries=AValue then exit;
InvalidateOptions;
inherited SetLibraryPaths(AValue);
end;
procedure TProjectCompilerOptions.SetLinkerOptions(const AValue: string);
begin
if LinkerOptions=AValue then exit;
InvalidateOptions;
inherited SetLinkerOptions(AValue);
end;
procedure TProjectCompilerOptions.SetObjectPath(const AValue: string);
begin
if ObjectPath=AValue then exit;
InvalidateOptions;
inherited SetObjectPath(AValue);
end;
procedure TProjectCompilerOptions.SetSrcPath(const AValue: string);
begin
if SrcPath=AValue then exit;
InvalidateOptions;
inherited SetSrcPath(AValue);
end;
procedure TProjectCompilerOptions.SetUnitPaths(const AValue: string);
begin
if OtherUnitFiles=AValue then exit;
InvalidateOptions;
inherited SetUnitPaths(AValue);
end;
procedure TProjectCompilerOptions.SetUnitOutputDir(const AValue: string);
begin
if UnitOutputDirectory=AValue then exit;
InvalidateOptions;
inherited SetUnitOutputDir(AValue);
if IsActive then
LazProject.DefineTemplates.OutputDirectoryChanged;
end;
procedure TProjectCompilerOptions.SetConditionals(AValue: string);
begin
AValue:=UTF8Trim(AValue,[]);
if Conditionals=AValue then exit;
InvalidateOptions;
inherited SetConditionals(AValue);
end;
function TProjectCompilerOptions.SubstituteProjectMacros(const s: string;
PlatformIndependent: boolean): string;
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.IsEqual(CompOpts: TBaseCompilerOptions): boolean;
begin
Result:=inherited IsEqual(CompOpts);
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.InvalidateOptions;
begin
if (LazProject=nil) then exit;
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 := [crCompile, crBuild, crRun];
inherited Create(AOwner, TProjectCompilationToolOptions);
with TProjectCompilationToolOptions(ExecuteBefore) do begin
DefaultCompileReasons:=crAll;
CompileReasons:=DefaultCompileReasons;
end;
with TProjectCompilationToolOptions(ExecuteAfter) do begin
DefaultCompileReasons:=crAll;
CompileReasons:=DefaultCompileReasons;
end;
if AOwner <> nil
then FProject := AOwner as TProject;
ParsedOpts.OnLocalSubstitute:=@SubstituteProjectMacros;
end;
destructor TProjectCompilerOptions.Destroy;
begin
inherited Destroy;
end;
function TProjectCompilerOptions.IsActive: boolean;
begin
Result:=(LazProject<>nil) and (LazProject.CompilerOptions=Self)
and not LazProject.BuildModes.Assigning;
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;
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;
procedure TProjectDefineTemplates.UpdateSrcDirIfDef;
var
Changed: Boolean;
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;
Changed:=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 unit path template for this directory
UnitPathDefTempl:=TDefineTemplate.Create('UnitPath', lisPkgDefsUnitPath,
'#UnitPath','$(#UnitPath);$ProjectUnitPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(UnitPathDefTempl);
// create include path template for this directory
IncPathDefTempl:=TDefineTemplate.Create('IncPath','Include Path',
'#IncPath','$(#IncPath);$ProjectIncPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(IncPathDefTempl);
// create src path template for this directory
SrcPathDefTempl:=TDefineTemplate.Create('SrcPath','Src Path',
'#SrcPath','$(#SrcPath);$ProjectSrcPath('+Owner.IDAsString+')',
da_Define);
FSrcDirIf.AddChild(SrcPathDefTempl);
Changed:=true;
end else begin
if FSrcDirIf.Value<>IfValue then begin
FSrcDirIf.Value:=IfValue;
Changed:=true;
end;
end;
if Changed then
CodeToolBoss.DefineTree.ClearCache;
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 directoy 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;
CodeToolBoss.DefineTree.ClearCache;
finally
NewSourceDirs.Free;
end;
end;
procedure TProjectDefineTemplates.UpdateDefinesForCustomDefines;
var
OptionsDefTempl: TDefineTemplate;
NewCustomOptions: String;
Changed: Boolean;
begin
if (not Owner.NeedsDefineTemplates) or (not Active) then exit;
// check if something has changed
NewCustomOptions:=Owner.BaseCompilerOptions.GetOptionsForCTDefines;
if (FLastCustomOptions=NewCustomOptions) then exit;
Changed:=false;
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 then begin
if FSrcDirIf.DeleteChild('Custom Options') then
Changed:=true;
end;
end else begin
UpdateSrcDirIfDef;
FSrcDirIf.ReplaceChild(OptionsDefTempl);
Changed:=true;
end;
if Changed then
CodeToolBoss.DefineTree.ClearCache;
end;
procedure TProjectDefineTemplates.FixTemplateOrder;
begin
if (FSrcDirIf<>nil) then
FSrcDirIf.Parent.MoveToLast(FSrcDirIf);
end;
procedure TProjectDefineTemplates.ClearFlags;
begin
FFlags:=FFlags+[ptfFlagsChanged];
end;
procedure TProjectDefineTemplates.AllChanged;
begin
SourceDirectoriesChanged;
CustomDefinesChanged;
UpdateGlobalValues;
UpdateSrcDirIfDef;
CodeToolBoss.DefineTree.ClearCache;
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).ProjectDirectory;
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: Boolean; var Cnt: integer);
var
SubPath: String;
begin
inc(Cnt);
SubPath:=Path+'BuildModes/Item'+IntToStr(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.OnItemChanged(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);
fOnChanged:=TMethodList.Create;
fItems:=TFPList.Create;
FChangeStamp:=CTInvalidChangeStamp;
fSavedChangeStamp:=FChangeStamp;
FSharedMatrixOptions:=TBuildMatrixOptions.Create;
FSharedMatrixOptions.OnChanged:=@OnItemChanged;
FSessionMatrixOptions:=TBuildMatrixOptions.Create;
FSessionMatrixOptions.OnChanged:=@OnItemChanged;
end;
destructor TProjectBuildModes.Destroy;
begin
FreeAndNil(fOnChanged);
Clear;
FreeAndNil(FSharedMatrixOptions);
FreeAndNil(FSessionMatrixOptions);
FreeAndNil(fItems);
inherited Destroy;
end;
procedure TProjectBuildModes.Clear;
begin
while Count>0 do Delete(Count-1);
SharedMatrixOptions.Clear;
SessionMatrixOptions.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);
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.ProjectDirectory;
Result.AddOnChangedHandler(@OnItemChanged);
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
CTIncreaseChangeStamp(FChangeStamp);
if fOnChanged<>nil then fOnChanged.CallNotifyEvents(Self);
end;
procedure TProjectBuildModes.AddOnChangedHandler(const Handler: TNotifyEvent);
begin
fOnChanged.Add(TMethod(Handler));
end;
procedure TProjectBuildModes.RemoveOnChangedHandler(const Handler: TNotifyEvent);
begin
fOnChanged.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;
// Debug flags
GenerateDebugInfo:=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:=dsDwarf2Set; // Debug
// Create Release mode
RelMode:=Add(ReleaseModeName);
AssignAndSetBooleans(RelMode, False);
RelMode.CompilerOptions.OptimizationLevel:=3; // Optimization, slow, but safe, -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;
begin
for i:=FromIndex to ToIndex do
begin
SubPath:=Path+'Item'+IntToStr(i)+'/';
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 (MacroName='') or 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;
begin
// First default mode.
LoadMacroValues(Path+'MacroValues/', Items[0]);
// Iterate rest of the modes.
for i:=2 to Cnt do
begin
SubPath:=Path+'BuildModes/Item'+IntToStr(i)+'/';
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;
end;
procedure TProjectBuildModes.LoadProjOptsFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
// Load for project
var
Cnt: Integer;
begin
FXMLConfig := XMLConfig;
Cnt:=FXMLConfig.GetValue(Path+'BuildModes/Count',0);
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/Item1/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;
begin
FXMLConfig := XMLConfig;
if LoadAllOptions then
// load matrix options
SessionMatrixOptions.LoadFromXMLConfig(FXMLConfig, Path+'BuildModes/SessionMatrixOptions/');
Cnt:=FXMLConfig.GetValue(Path+'BuildModes/Count',0);
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
// 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: 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, Cnt);
FXMLConfig.SetDeleteValue(Path+'BuildModes/Count',Cnt,0);
end;
procedure TProjectBuildModes.SaveSessionOptsToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; SaveSession: 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, Cnt);
FXMLConfig.SetDeleteValue(Path+'BuildModes/Count',Cnt,0);
end;
initialization
RegisterIDEOptionsGroup(GroupProject, TProjectIDEOptions);
RegisterIDEOptionsGroup(GroupCompiler, TProjectCompilerOptions);
end.