lazarus/ide/projectresources.pas

888 lines
27 KiB
ObjectPascal

{
/***************************************************************************
projectresources.pas - Lazarus IDE unit
-----------------------------------------
***************************************************************************/
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
Abstract: Project Resources - is a list of System and Lazarus resources.
Currently it contains:
- Version information
- XP manifest
- Project icon
}
unit ProjectResources;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, resource, reswriter, fgl, AVL_Tree,
// LCL
Controls, LResources,
// LazUtils
LazFileUtils, Laz2_XMLCfg, LazLoggerBase, LazFileCache,
// Codetools
KeywordFuncLists, BasicCodeTools, CodeToolManager, CodeCache,
// IdeIntf
ProjectIntf, ProjectResourcesIntf, CompOptsIntf,
// IDE
LazarusIDEStrConsts, DialogProcs,
W32Manifest, W32VersionInfo, ProjectIcon, ProjectUserResources;
type
TResourceList = specialize TFPGObjectList<TAbstractProjectResource>;
{ TProjectResources }
TProjectResources = class(TAbstractProjectResources)
private
FModified: Boolean;
FOnModified: TNotifyEvent;
FInModified: Boolean;
FLrsIncludeAllowed: Boolean;
FResources: TResourceList;
FSystemResources: TResources;
FLazarusResources: TStringList;
resFileName, lrsFileName: String;
LastResFileName, LastLrsFileName: String;
LastSavedRes: String;
function GetProjectIcon: TProjectIcon;
function GetProjectUserResources: TProjectUserResources;
function GetVersionInfo: TProjectVersionInfo;
function GetXPManifest: TProjectXPManifest;
procedure SetFileNames(const MainFileName, TestDir: String);
procedure SetModified(const AValue: Boolean);
function Update: Boolean;
function UpdateMainSourceFile(const AFileName: string): Boolean;
procedure UpdateFlagLrsIncludeAllowed(const AFileName: string);
function Save(SaveToTestDir: string): Boolean;
function UpdateResCodeBuffer: Boolean;
procedure UpdateLrsCodeBuffer;
procedure DeleteLastCodeBuffers;
procedure ResourceModified(Sender: TObject);
protected
procedure SetResourceType(const AValue: TResourceType); override;
function GetProjectResource(AIndex: TAbstractProjectResourceClass): TAbstractProjectResource; override;
public
constructor Create(AProject: TLazProject); override;
destructor Destroy; override;
procedure AddSystemResource(AResource: TAbstractResource); override;
procedure AddLazarusResource(AResource: TStream; const AResourceName, AResourceType: String); override;
procedure DoAfterBuild(AReason: TCompileReason; SaveToTestDir: boolean);
procedure DoBeforeBuild(AReason: TCompileReason; SaveToTestDir: boolean);
procedure Clear;
function Regenerate(const MainFileName: String;
UpdateSource, PerformSave: boolean; const SaveToTestDir: string): Boolean;
function RenameDirectives(const CurFileName, NewFileName: String): Boolean;
procedure DeleteResourceBuffers;
function HasSystemResources: Boolean;
function HasLazarusResources: Boolean;
procedure WriteToProjectFile(AConfig: TXMLConfig; const Path: String);
procedure ReadFromProjectFile(AConfig: TXMLConfig; const Path: String; ReadAll: Boolean);
property Modified: Boolean read FModified write SetModified;
property OnModified: TNotifyEvent read FOnModified write FOnModified;
property XPManifest: TProjectXPManifest read GetXPManifest;
property VersionInfo: TProjectVersionInfo read GetVersionInfo;
property ProjectIcon: TProjectIcon read GetProjectIcon;
property UserResources: TProjectUserResources read GetProjectUserResources;
end;
function GuessResourceType(Code: TCodeBuffer; out Typ: TResourceType): boolean;
const
ResourceTypeNames: array[TResourceType] of string = (
'lrs',
'res'
);
function StrToResourceType(const s: string): TResourceType;
implementation
const
LazResourcesUnit = 'LResources';
function StrToResourceType(const s: string): TResourceType;
var
t: TResourceType;
begin
for t := Low(TResourceType) to High(TResourceType) do
if SysUtils.CompareText(ResourceTypeNames[t], s) = 0 then exit(t);
Result := rtLRS;
end;
procedure ParseResourceType(Code: TCodeBuffer; NestedComments: boolean;
out HasLRSIncludeDirective, HasRDirective: boolean);
function ExtractDirectiveFileName(ds: PChar): string;
var i: Integer;
begin
while IsIdentChar[ds^] do Inc(ds);
while ds^ in [' ',#9] do Inc(ds);
if ds^ = '''' then
begin
Inc(ds);
i := IndexChar(ds^, -1, '''');
SetLength(Result{%H-}, i);
if i>0 then
Move(ds^, Result[1], i);
end else begin
i := IndexChar(ds^, -1, '}');
SetLength(Result, i);
if i>0 then
Move(ds^, Result[1], i);
Result := TrimRight(Result);
end;
end;
var
p: Integer;
d: PChar;
Src, dFileName: string;
begin
Src := Code.Source;
HasLRSIncludeDirective := False;
HasRDirective := False;
p:=1;
while p < length(Src) do
begin
p := FindNextCompilerDirective(Src, p, NestedComments);
if p > length(Src) then break;
d := @Src[p];
if (d[0]='{') and (d[1]='$') then
begin
inc(d, 2);
if (d[0] in ['r','R']) and not (HasRDirective or IsIdentChar[d[1]]) then
begin
// using resources
dFileName := ExtractDirectiveFileName(d);
HasRDirective := SameText(dFileName, '*.lfm') or
SameText(dFileName, ExtractFileNameOnly(Code.Filename) + '.lfm');
end
else
if (d[0] in ['i','I']) and not HasLRSIncludeDirective
and ((d[1] in [' ',#9]) or (CompareIdentifiers(@d[0],'include')=0)) then
begin
// using include directive with lrs file
dFileName := ExtractDirectiveFileName(d);
HasLRSIncludeDirective :=
SameText(dFileName, ExtractFileNameOnly(Code.Filename) + '.lrs') or
SameText(dFileName, '*.lrs');
end;
end;
p := FindCommentEnd(Src, p, NestedComments);
end;
end;
type
TResourceTypesCacheItem = class
public
Code: TCodeBuffer;
CodeStamp: integer;
HasLRSIncludeDirective: boolean;
HasRDirective: boolean;
end;
function CompareResTypCacheItems(Data1, Data2: Pointer): integer;
var
Item1: TResourceTypesCacheItem absolute Data1;
Item2: TResourceTypesCacheItem absolute Data2;
begin
Result:=CompareFilenames(Item1.Code.Filename,Item2.Code.Filename);
end;
function CompareCodeWithResTypCacheItem(CodeBuf, CacheItem: Pointer): integer;
var
Code: TCodeBuffer absolute CodeBuf;
Item: TResourceTypesCacheItem absolute CacheItem;
begin
Result:=CompareFilenames(Code.Filename,Item.Code.Filename);
end;
type
{ TResourceTypesCache }
TResourceTypesCache = class
public
Tree: TAvlTree; //
constructor Create;
destructor Destroy; override;
procedure Parse(Code: TCodeBuffer;
out HasLRSIncludeDirective, HasRDirective: boolean);
end;
{ TResourceTypesCache }
constructor TResourceTypesCache.Create;
begin
Tree:=TAvlTree.Create(@CompareResTypCacheItems);
end;
destructor TResourceTypesCache.Destroy;
begin
Tree.FreeAndClear;
FreeAndNil(Tree);
inherited Destroy;
end;
procedure TResourceTypesCache.Parse(Code: TCodeBuffer; out
HasLRSIncludeDirective, HasRDirective: boolean);
var
Node: TAvlTreeNode;
Item: TResourceTypesCacheItem;
begin
Node := Tree.FindKey(Code, @CompareCodeWithResTypCacheItem);
if (Node <> nil) then
begin
Item := TResourceTypesCacheItem(Node.Data);
if (Item.CodeStamp = Item.Code.ChangeStep) then
begin
// cache valid
HasLRSIncludeDirective := Item.HasLRSIncludeDirective;
HasRDirective := Item.HasRDirective;
exit;
end;
end
else
Item := nil;
// update
if Item = nil then
begin
Item := TResourceTypesCacheItem.Create;
Item.Code := Code;
Tree.Add(Item);
end;
Item.CodeStamp := Code.ChangeStep;
ParseResourceType(Code,
CodeToolBoss.GetNestedCommentsFlagForFile(Code.Filename),
Item.HasLRSIncludeDirective, Item.HasRDirective);
HasLRSIncludeDirective := Item.HasLRSIncludeDirective;
HasRDirective := Item.HasRDirective;
end;
var
ResourceTypesCache: TResourceTypesCache = nil;
function GuessResourceType(Code: TCodeBuffer; out Typ: TResourceType): boolean;
var
HasLRSIncludeDirective, HasRDirective: Boolean;
begin
if ResourceTypesCache = nil then
ResourceTypesCache := TResourceTypesCache.Create;
ResourceTypesCache.Parse(Code, HasLRSIncludeDirective, HasRDirective);
//DebugLn(['GuessResourceType ',Code.Filename,' HasLRS=',HasLRSIncludeDirective,' HasR=',HasRDirective]);
if HasLRSIncludeDirective then
begin
Typ := rtLRS;
Result := True;
end
else
if HasRDirective then
begin
Typ := rtRes;
Result := True;
end
else
begin
Typ := rtLRS;
Result := False;
end;
end;
{ TProjectResources }
procedure TProjectResources.SetFileNames(const MainFileName, TestDir: String);
begin
// rc is in the executable dir
//resFileName := TestDir + ExtractFileNameOnly(MainFileName) + '.rc';
// res is in the project dir for now because {$R project1.res} searches only in unit dir
// lrs is in the project dir also
if FileNameIsAbsolute(MainFileName) then
begin
resFileName := ChangeFileExt(MainFileName, '.res');
lrsFileName := ChangeFileExt(MainFileName, '.lrs');
end
else
begin
resFileName := TestDir + ExtractFileNameOnly(MainFileName) + '.res';
lrsFileName := TestDir + ExtractFileNameOnly(MainFileName) + '.lrs';
end;
end;
function TProjectResources.GetProjectIcon: TProjectIcon;
begin
Result := TProjectIcon(GetProjectResource(TProjectIcon));
end;
function TProjectResources.GetProjectUserResources: TProjectUserResources;
begin
Result := TProjectUserResources(GetProjectResource(TProjectUserResources));
end;
function TProjectResources.GetVersionInfo: TProjectVersionInfo;
begin
Result := TProjectVersionInfo(GetProjectResource(TProjectVersionInfo));
end;
function TProjectResources.GetXPManifest: TProjectXPManifest;
begin
Result := TProjectXPManifest(GetProjectResource(TProjectXPManifest));
end;
procedure TProjectResources.SetResourceType(const AValue: TResourceType);
begin
if ResourceType <> AValue then
begin
inherited SetResourceType(AValue);
Modified := True;
end;
end;
procedure TProjectResources.SetModified(const AValue: Boolean);
var
i: integer;
begin
if FInModified then
Exit;
FInModified := True;
if FModified <> AValue then
begin
FModified := AValue;
if not FModified then
for i := 0 to FResources.Count - 1 do
FResources[i].Modified := False;
if Assigned(FOnModified) then
OnModified(Self);
end;
FInModified := False;
end;
function TProjectResources.Update: Boolean;
var
i: integer;
begin
Result:=true;
Clear;
for i := 0 to FResources.Count - 1 do
begin
Result := FResources[i].UpdateResources(Self, resFileName);
if not Result then begin
debugln(['Error: (lazarus) [TProjectResources.Update] UpdateResources of ',DbgSName(FResources[i]),' failed']);
Exit;
end;
end;
end;
procedure TProjectResources.ResourceModified(Sender: TObject);
begin
if TAbstractProjectResource(Sender).Modified then
Modified := true;
end;
constructor TProjectResources.Create(AProject: TLazProject);
var
i: integer;
L: TList;
R: TAbstractProjectResource;
begin
inherited Create(AProject);
inherited SetResourceType(rtRes); // set fpc resources by default
FInModified := False;
FLrsIncludeAllowed := False;
FSystemResources := TResources.Create;
FLazarusResources := TStringList.Create;
FResources := TResourceList.Create;
L := GetRegisteredResources;
for i := 0 to L.Count - 1 do
begin
R := TAbstractProjectResourceClass(L[i]).Create;
R.Modified := False;
R.OnModified := @ResourceModified;
FResources.Add(R);
end;
end;
destructor TProjectResources.Destroy;
begin
DeleteResourceBuffers;
FreeAndNil(FResources);
FreeAndNil(FSystemResources);
FreeAndNil(FLazarusResources);
inherited Destroy;
end;
procedure TProjectResources.AddSystemResource(AResource: TAbstractResource);
begin
FSystemResources.Add(AResource);
end;
procedure TProjectResources.AddLazarusResource(AResource: TStream;
const AResourceName, AResourceType: String);
var
OutStream: TStringStream;
begin
OutStream := TStringStream.Create('');
try
BinaryToLazarusResourceCode(AResource, OutStream, AResourceName, AResourceType);
FLazarusResources.Add(OutStream.DataString);
finally
OutStream.Free;
end;
end;
function TProjectResources.GetProjectResource(AIndex: TAbstractProjectResourceClass): TAbstractProjectResource;
var
i: integer;
begin
for i := 0 to FResources.Count - 1 do
begin
Result := FResources[i];
if Result.InheritsFrom(AIndex) then
Exit;
end;
Result := nil;
end;
procedure TProjectResources.DoAfterBuild(AReason: TCompileReason; SaveToTestDir: boolean);
var
i: integer;
begin
for i := 0 to FResources.Count - 1 do
FResources[i].DoAfterBuild(Self, AReason, SaveToTestDir);
end;
procedure TProjectResources.DoBeforeBuild(AReason: TCompileReason; SaveToTestDir: boolean);
var
i: integer;
begin
for i := 0 to FResources.Count - 1 do
FResources[i].DoBeforeBuild(Self, AReason, SaveToTestDir);
end;
procedure TProjectResources.Clear;
begin
FSystemResources.Clear;
FLazarusResources.Clear;
FMessages.Clear;
end;
function TProjectResources.Regenerate(const MainFileName: String;
UpdateSource, PerformSave: boolean; const SaveToTestDir: string): Boolean;
begin
//DebugLn(['TProjectResources.Regenerate MainFilename=',MainFilename,
// ' UpdateSource=',UpdateSource,' PerformSave=',PerformSave]);
//DumpStack;
Result := False;
Assert(MainFileName<>'', 'TProjectResources.Regenerate: MainFileName is empty.');
// remember old codebuffer filenames
LastResFileName := resFileName;
LastLrsFileName := lrsFileName;
SetFileNames(MainFileName, SaveToTestDir);
UpdateFlagLrsIncludeAllowed(MainFileName);
try
// update resources (FLazarusResources, FSystemResources, ...)
if not Update then begin
debugln(['Error: (lazarus) [TProjectResources.Regenerate] Update failed']);
Exit;
end;
if LastSavedRes='' then begin
// ToDo: Read an existing .res file from disk into LastSavedRes
// to know if a new resource should be saved.
// Now it gets saved once the first time after IDE started.
end;
// codebuffer of new .res file
if not UpdateResCodeBuffer then
PerformSave := False; // Do not save an unchanged resource file.
// codebuffer of new .lrs file
UpdateLrsCodeBuffer;
// update .lpr file (old and new include files exist, so parsing should work without errors)
if UpdateSource and not UpdateMainSourceFile(MainFileName) then begin
debugln(['Error: (lazarus) [TProjectResources.Regenerate UpdateMainSourceFile] failed']);
exit;
end;
if PerformSave and not Save(SaveToTestDir) then begin
debugln(['Error: (lazarus) [TProjectResources.Regenerate] Save failed']);
Exit;
end;
finally
DeleteLastCodeBuffers;
end;
Result := True;
end;
function TProjectResources.HasSystemResources: Boolean;
begin
Result := FSystemResources.Count > 0;
end;
function TProjectResources.HasLazarusResources: Boolean;
begin
Result := FLazarusResources.Count > 0;
end;
procedure TProjectResources.WriteToProjectFile(AConfig: TXMLConfig;
const Path: String);
var
i: integer;
begin
AConfig.SetDeleteValue(Path+'General/ResourceType/Value', ResourceTypeNames[ResourceType], ResourceTypeNames[rtLRS]);
for i := 0 to FResources.Count - 1 do
FResources[i].WriteToProjectFile(AConfig, Path);
end;
procedure TProjectResources.ReadFromProjectFile(AConfig: TXMLConfig;
const Path: String; ReadAll: Boolean);
var
i: integer;
begin
ResourceType := StrToResourceType(AConfig.GetValue(Path+'General/ResourceType/Value', ResourceTypeNames[rtLRS]));
for i := 0 to FResources.Count - 1 do
if ReadAll or FResources[i].IsDefaultOption then
FResources[i].ReadFromProjectFile(AConfig, Path);
end;
function TProjectResources.UpdateMainSourceFile(const AFileName: string): Boolean;
var
NewX, NewY, NewTopLine: integer;
CodeBuf, NewCode: TCodeBuffer;
Filename, Directive: String;
NamePos, InPos: integer;
begin
Result := True;
CodeBuf := CodeToolBoss.LoadFile(AFilename, False, False);
if CodeBuf <> nil then
begin
SetFileNames(AFileName, '');
Filename := ExtractFileName(resFileName);
//debugln(['TProjectResources.UpdateMainSourceFile HasSystemResources=',HasSystemResources,' Filename=',Filename,' HasLazarusResources=',HasLazarusResources]);
// update LResources uses
if FLrsIncludeAllowed and HasLazarusResources then
begin
if CodeToolBoss.FindUnitInAllUsesSections(CodeBuf, LazResourcesUnit, NamePos, InPos)
and not CodeToolBoss.AddUnitToMainUsesSection(CodeBuf, LazResourcesUnit,'') then
begin
Result := False;
Messages.Add(Format(lisCouldNotAddToMainSource, [LazResourcesUnit]));
debugln(['Error: (lazarus) [TProjectResources.UpdateMainSourceFile] adding LResources to main source failed']);
end;
end;
// update {$R filename} directive
if CodeToolBoss.FindResourceDirective(CodeBuf, 1, 1,
NewCode, NewX, NewY,
NewTopLine, '*.res', false) then
begin
// there is a resource directive in the source
if not HasSystemResources then
begin
if not CodeToolBoss.RemoveDirective(NewCode, NewX, NewY, true) then
begin
Result := False;
Messages.Add(Format(lisCouldNotRemoveRFromMainSource, [Filename]));
debugln(['Error: (lazarus) [TProjectResources.UpdateMainSourceFile] failed: removing resource directive']);
end;
end;
end
else
if HasSystemResources then
begin
Directive := '{$R *.res}';
if not CodeToolBoss.AddResourceDirective(CodeBuf, Filename, false, Directive) then
begin
Result := False;
Messages.Add(Format(lisCouldNotAddRToMainSource, [Filename]));
debugln(['Error: (lazarus) [TProjectResources.UpdateMainSourceFile] failed: adding resource directive']);
end;
end;
// update {$I filename} directive
Filename := ExtractFileName(lrsFileName);
if CodeToolBoss.FindIncludeDirective(CodeBuf, 1, 1,
NewCode, NewX, NewY,
NewTopLine, Filename, false) then
begin
// there is a resource directive in the source
//debugln(['TProjectResources.UpdateMainSourceFile include directive found: FCanHaveLrsInclude=',FLrsIncludeAllowed,' HasLazarusResources=',HasLazarusResources]);
if not (FLrsIncludeAllowed and HasLazarusResources) then
begin
if not CodeToolBoss.RemoveDirective(NewCode, NewX, NewY, true) then
begin
Result := False;
Messages.Add(Format(lisCouldNotRemoveIFromMainSource, [Filename]));
debugln(['Error: (lazarus) [TProjectResources.UpdateMainSourceFile] removing include directive from main source failed']);
Exit;
end;
end;
end
else
if FLrsIncludeAllowed and HasLazarusResources then
begin
//debugln(['TProjectResources.UpdateMainSourceFile include directive not found: FCanHaveLrsInclude=',FLrsIncludeAllowed,' HasLazarusResources=',HasLazarusResources]);
if not CodeToolBoss.AddIncludeDirectiveForInit(CodeBuf,Filename,'') then
begin
Result := False;
Messages.Add(Format(lisCouldNotAddIToMainSource, [Filename]));
debugln(['Error: (lazarus) [TProjectResources.UpdateMainSourceFile] adding include directive to main source failed']);
Exit;
end;
end;
end;
end;
procedure TProjectResources.UpdateFlagLrsIncludeAllowed(const AFileName: string);
var
CodeBuf: TCodeBuffer;
NamePos, InPos: Integer;
begin
FLrsIncludeAllowed := False;
CodeBuf := CodeToolBoss.LoadFile(AFileName, False, False);
if CodeBuf = nil then
Exit;
// Check that .lpr contains Forms and Interfaces in the uses section. If it does not
// we cannot add LResources (it is not a lazarus application)
FLrsIncludeAllowed :=
CodeToolBoss.FindUnitInAllUsesSections(CodeBuf, 'Forms', NamePos, InPos, True) and
CodeToolBoss.FindUnitInAllUsesSections(CodeBuf, 'Interfaces', NamePos, InPos, True);
end;
function TProjectResources.RenameDirectives(const CurFileName, NewFileName: String): Boolean;
var
NewX, NewY, NewTopLine: integer;
CodeBuf, NewCode: TCodeBuffer;
oldLrsFileName, newLrsFileName: String;
begin
//DebugLn(['TProjectResources.RenameDirectives CurFileName="',CurFileName,'" NewFileName="',NewFileName,'"']);
Result := True;
CodeBuf := CodeToolBoss.LoadFile(CurFileName, False, False);
if CodeBuf = nil then
Exit;
LastResFileName := resFileName;
LastLrsFileName := lrsFileName;
try
SetFileNames(CurFileName, '');
oldLrsFileName := ExtractFileName(lrsFileName);
SetFileNames(NewFileName, '');
newLrsFileName := ExtractFileName(lrsFileName);
// update resources (FLazarusResources, FSystemResources, ...)
UpdateFlagLrsIncludeAllowed(CurFileName);
if not Update then
Exit;
// update codebuffers of new .res and .lrs files
UpdateResCodeBuffer;
UpdateLrsCodeBuffer;
LastSavedRes := '';
// update {$I filename} directive
if CodeToolBoss.FindIncludeDirective(CodeBuf, 1, 1,
NewCode, NewX, NewY,
NewTopLine, oldLrsFileName, false) then
begin
// there is a resource directive in the source
if not CodeToolBoss.RemoveDirective(NewCode, NewX, NewY, true) then
begin
Result := False;
debugln(['TProjectResources.RenameDirectives removing include directive from main source failed']);
Messages.Add('Could not remove "{$I '+ oldLrsFileName +'"} from main source!');
Exit;
end;
if not CodeToolBoss.AddIncludeDirectiveForInit(CodeBuf, newLrsFileName, '') then
begin
Result := False;
debugln(['TProjectResources.RenameDirectives adding include directive to main source failed']);
Messages.Add('Could not add "{$I '+ newLrsFileName +'"} to main source!');
Exit;
end;
end;
finally
DeleteLastCodeBuffers;
end;
end;
procedure TProjectResources.DeleteResourceBuffers;
procedure DeleteBuffer(Filename: string);
var
CodeBuf: TCodeBuffer;
begin
if Filename = '' then
Exit;
CodeBuf := CodeToolBoss.FindFile(Filename);
if CodeBuf <> nil then
CodeBuf.IsDeleted := true;
end;
begin
DeleteLastCodeBuffers;
DeleteBuffer(resFileName);
DeleteBuffer(lrsFileName);
end;
function TProjectResources.Save(SaveToTestDir: string): Boolean;
function SaveCodeBuf(CodeBuf: TCodeBuffer): boolean;
var
TestFilename: String;
begin
Result := True;
if not CodeBuf.IsVirtual then
Result := SaveCodeBuffer(CodeBuf) in [mrOk,mrIgnore]
else if SaveToTestDir<>'' then
begin
TestFilename := AppendPathDelim(SaveToTestDir) + CodeBuf.Filename;
Result := SaveCodeBufferToFile(CodeBuf, TestFilename) in [mrOk, mrIgnore];
end;
end;
var
CodeBuf: TCodeBuffer;
begin
Result := False;
// Save .res
CodeBuf := CodeToolBoss.FindFile(resFilename);
if Assigned(CodeBuf) and not CodeBuf.IsDeleted then
begin
Result := SaveCodeBuf(CodeBuf);
if not Result then Exit;
//DebugLn(['TProjectResources.Save: Res len=', Length(CodeBuf.Source),
// ', LastRes len=', Length(LastSavedRes)]);
LastSavedRes := CodeBuf.Source;
end;
// Save .lrs
CodeBuf := CodeToolBoss.FindFile(lrsFilename);
if Assigned(CodeBuf) and not CodeBuf.IsDeleted then
Result := SaveCodeBuf(CodeBuf);
end;
function TProjectResources.UpdateResCodeBuffer: Boolean;
// Generate .res resource and return True if it differs from the last saved one.
var
CodeBuf: TCodeBuffer;
ResStream: TMemoryStream;
Writer: TAbstractResourceWriter;
begin
Result := False;
if not HasSystemResources then Exit;
ResStream := TMemoryStream.Create;
Writer := TResResourceWriter.Create;
try
CodeBuf := CodeToolBoss.FindFile(resFileName);
if (CodeBuf=nil) or CodeBuf.FileOnDiskHasChanged then
begin
CodeBuf := CodeToolBoss.CreateFile(resFileName);
if (not CodeBuf.IsVirtual) and FileExists(resFileName) then
begin
// load old res file, for checking if something has changed
try
ResStream.LoadFromFile(resFileName);
except
// load error -> no problem, writing is imprtant
end;
CodeBuf.LoadFromStream(ResStream);
ResStream.Clear;
end;
end;
try
FSystemResources.WriteToStream(ResStream, Writer);
except
on E: Exception do
begin
debugln('Error: (lazarus) [TProjectResources.UpdateResCodeBuffer] exception %s: %s', [E.ClassName, E.Message]);
ResStream.Size := 0;
end;
end;
ResStream.Position := 0;
CodeBuf.LoadFromStream(ResStream);
Result := CodeBuf.FileNeedsUpdate;
finally
Writer.Free;
ResStream.Free;
end;
end;
procedure TProjectResources.UpdateLrsCodeBuffer;
// Generate .lrs resource.
var
CodeBuf: TCodeBuffer;
begin
if not (FLrsIncludeAllowed and HasLazarusResources) then Exit;
CodeBuf := CodeToolBoss.CreateFile(lrsFileName);
CodeBuf.Source := FLazarusResources.Text;
end;
procedure TProjectResources.DeleteLastCodeBuffers;
procedure CleanCodeBuffer(var OldFilename: string; const NewFilename: string);
var
CodeBuf: TCodeBuffer;
begin
if (OldFileName <> '') and (OldFilename <> NewFilename) then
begin
// file was renamed => mark old file as deleted
CodeBuf := CodeToolBoss.FindFile(OldFileName);
if (CodeBuf <> nil) then
CodeBuf.IsDeleted := true;
OldFileName := '';
end;
end;
begin
CleanCodeBuffer(LastResFileName, resFileName);
CleanCodeBuffer(LastLrsFileName, lrsFileName);
end;
finalization
ResourceTypesCache.Free;
end.