lazarus/tools/glazres/glazresmain.pp

556 lines
16 KiB
ObjectPascal

{
***************************************************************************
* *
* 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. *
* *
***************************************************************************
Author of LazRes: Mattias Gaertner
Original idea for GLazRes: Andy Koz
Adapted by: Bart Broersma
GLazRes aims to be a GUI implementation of the LazRes program.
}
unit glazresmain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Types, IniFiles,
Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, ExtDlgs, EditBtn,
LResources, LCLProc, LCLType,
LazFileUtils, LazUTF8;
type
{ TGLazResForm }
TGLazResForm = class(TForm)
ClearBtn: TBitBtn;
CloseBtn: TBitBtn;
DestEdt: TFileNameEdit;
MessagesLabel: TLabel;
OpenPictureDialog: TOpenPictureDialog;
AddImgBtn: TBitBtn;
StartBtn: TBitBtn;
FilesLabel: TLabel;
FileListBox: TListBox;
MsgMemo: TMemo;
AddAnyBtn: TBitBtn;
LrsLabel: TLabel;
OpenDialog: TOpenDialog;
DeleteBtn: TBitBtn;
procedure AddImgBtnClick(Sender: TObject);
procedure ClearBtnClick(Sender: TObject);
procedure DeleteBtnClick(Sender: TObject);
procedure DestEdtAcceptFileName(Sender: TObject; var {%H-}Value: String);
procedure DestEdtEditingDone(Sender: TObject);
procedure FileListBoxDrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; {%H-}State: TOwnerDrawState);
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure AddAnyBtnClick(Sender: TObject);
procedure StartBtnClick(Sender: TObject);
private
FIniFileName: String;
procedure CreateAnchors;
procedure ResizeControls({%H-}Dummy: PtrInt);
procedure CreateLazarusResourceFile;
procedure ConvertFormToText(Stream: TMemoryStream);
procedure AddFiles(Names: TStrings);
procedure MaybeEnableButtons;
procedure AddMessage(const Msg: String);
procedure AddMessageFmt(const Msg: String; Args: Array of const);
procedure ClearMessages;
procedure LoadWindowGeometry;
procedure SaveWindowGeometry;
public
end;
var
GLazResForm: TGLazResForm;
implementation
{$R *.lfm}
resourcestring
ErrConvertToText = 'ERROR: unable to convert Delphi form to text: "%s"';
ErrFileNotFound = 'ERROR: File not found: "%s"';
ErrFileIsResource = 'ERROR: Cannot add resource file to itself ("%s")';
ErrCreate = 'ERROR: Cannot create "%s"';
ErrNoResourceName = 'ERROR: No resourcename found for "%s"';
MsgCreatingLrs = 'Creating "%s"';
MsgProcessing = 'Processing "%s"';
MsgResourceNameType = ' Resource name = "%s", Type = "%s"';
ErrRead = 'ERROR: Cannot read from "%s"';
MsgSuccess = 'Done.'+ LineEnding + 'Number of resources added: %d.';
MsgWrongExt = 'Filename does not have the required extension: fix it?';
DESaveResourcefileAs = 'Save resourcefile as';
DEFilter = 'Lazarus Resource Files|*.lrs|All Files|*';
ODOpenExistingFile = 'Open existing file';
OPDOpenExistingPicture = 'Open existing picture';
OPDFilter ='Graphic (*.png;*.xpm;*.bmp;*.cur;*.ico;*.icns;*.jpeg;*.jpg;*.jpe;*.jfif;*.tif;*.tiff;*.gif;*.pbm;*.pgm;*.ppm;*.gif;*.tga)|*.png;*.xpm;*.bmp;*.cur;*.ico;*.icns;*.jpeg;*.jpg;*.jpe;*.jfif;*.tif;*.tiff;*.gif;*.pbm;*.pgm;*.ppm;*.gif;*.tga|Portable Network Graphic (*.png)|*.png|Pixmap (*.xpm)|*.xpm|Bitmaps (*.bmp)|*.bmp|Cursor (*.cur)|*.cur|Icon (*.ico)|*.ico|Mac OS X Icon (*.icns)|*.icns|Joint Picture Expert Group (*.jpeg;*.jpg;*.jpe;*.jfif)|*.jpeg;*.jpg;*.jpe;*.jfif|Tagged Image File Format (*.tif;*.tiff)|*.tif;*.tiff|Graphics Interchange Format (*.gif)|*.gif|Portable PixMap (*.pbm;*.pgm;*.ppm)|*.pbm;*.pgm;*.ppm|Animated GIF (*.gif)|*.gif|TGA Image File (*.tga)|*.tga|';
OPDFilterAll = 'All files';
CBtnCancel = 'Cancel';
const
AppName = 'GLazRes';
IniName = {$ifdef windows}'GLazRes.ini'{$else}'glazres.conf'{$endif};
scPosition = 'Position';
idLeft = 'Left';
idTop = 'Top';
idWidth = 'Width';
idHeight = 'Height';
//Needed for GetAppConfigDir
function GetVendorName: String;
begin
Result := '';
end;
function GetAppName: String;
begin
Result := AppName;
end;
{TGLazResForm}
// *************** Component Events *********************** //
procedure TGLazResForm.FormCreate(Sender: TObject);
begin
OnGetVendorName := @GetVendorName;
OnGetApplicationName := @GetAppName;
FIniFileName := GetAppConfigDir(False) + IniName;
CreateAnchors;
LoadWindowGeometry;
DestEdt.DialogTitle := DESaveResourcefileAs;
DestEdt.Filter := DEFilter;
OpenDialog.Title := ODOpenExistingFile;
//OpenDialog.Filter := OPDFilterAll + {$IFDEF WINDOWS} ' (*.*)|*.*|' {$ELSE} ' (*)|*|' {$ENDIF} ;
OpenPictureDialog.Title := OPDOpenExistingPicture;
OpenPictureDialog.Filter := OPDFilter + OPDFilterAll + {$IFDEF WINDOWS} ' (*.*)|*.*|' {$ELSE} ' (*)|*|' {$ENDIF} ;
CloseBtn.Caption := CBtnCancel;
end;
procedure TGLazResForm.FormShow(Sender: TObject);
begin
MaybeEnableButtons;
OnResize := @FormResize;
//Using QueueAsyncCall delays the layout until the form is shown,
//before that ClientWidht may have wrong value (depending on widgetset and windowmanager)
Application.QueueAsyncCall(@ResizeControls,0);
end;
procedure TGLazResForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
SaveWindowGeometry;
end;
procedure TGLazResForm.FormResize(Sender: TObject);
begin
ResizeControls(0);
end;
procedure TGLazResForm.DestEdtAcceptFileName(Sender: TObject; var Value: String);
begin
DestEdtEditingDone(DestEdt);
end;
procedure TGLazResForm.DestEdtEditingDone(Sender: TObject);
var
Fn, Ext: String;
begin
Fn := DestEdt.FileName;
Ext := ExtractFileExt(Fn);
if (Fn <> '') and (CompareText(Ext, '.lrs') <> 0) then
begin
if MessageDlg(AppName,MsgWrongExt,mtConfirmation,[mbYes,mbNo],0) = mrYes then
begin
Fn := ChangeFileExt(Fn, '.lrs');
DestEdt.FileName := Fn;
end;
end;
MaybeEnableButtons;
end;
procedure TGLazResForm.StartBtnClick(Sender: TObject);
begin
CreateLazarusResourceFile;
end;
procedure TGLazResForm.AddAnyBtnClick(Sender: TObject);
begin
if OpenDialog.Execute then
begin
AddFiles(OpenDialog.Files);
end;
end;
procedure TGLazResForm.AddImgBtnClick(Sender: TObject);
begin
if OpenPictureDialog.Execute then
begin
AddFiles(OpenPictureDialog.Files);
end;
end;
procedure TGLazResForm.DeleteBtnClick(Sender: TObject);
var
Index: integer;
begin
for Index := FileListBox.Count - 1 downto 0 do
begin
if FileListBox.Selected[Index] then
FileListBox.Items.Delete(Index);
end;
MaybeEnableButtons;
end;
procedure TGLazResForm.ClearBtnClick(Sender: TObject);
begin
FileListBox.Items.Clear;
MaybeEnableButtons;
end;
procedure TGLazResForm.FileListBoxDrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
var
OldBrushStyle: TBrushStyle;
OldTextStyle: TTextStyle;
NewTextStyle: TTextStyle;
ItemText: String;
TheCanvas: TCanvas;
begin
//Objective: draw only the FileName, not the fully qualified path.
TheCanvas := (Control as TCustomListBox).Canvas;
ItemText := ExtractFileName(FileListBox.Items[Index]);
if not(odBackgroundPainted in State) then
TheCanvas.FillRect(ARect);
OldBrushStyle := TheCanvas.Brush.Style;
TheCanvas.Brush.Style := bsClear;
OldTextStyle := TheCanvas.TextStyle;
NewTextStyle := OldTextStyle;
NewTextStyle.Layout := tlCenter;
NewTextStyle.RightToLeft := Control.UseRightToLeftReading;
if Control.UseRightToLeftAlignment then
begin
NewTextStyle.Alignment := taRightJustify;
ARect.Right := ARect.Right - 2;
end
else
begin
NewTextStyle.Alignment := taLeftJustify;
ARect.Left := ARect.Left + 2;
end;
TheCanvas.TextStyle := NewTextStyle;
TheCanvas.TextRect(ARect, ARect.Left, ARect.Top, ItemText);
TheCanvas.Brush.Style := OldBrushStyle;
TheCanvas.TextStyle := OldTextStyle;
end;
// ***************** Form layout and looks *********************** //
procedure TGLazResForm.CreateAnchors;
begin
DestEdt.AnchorToNeighbour(akTop, 5, LrsLabel);
FilesLabel.AnchorToNeighbour(akTop, 10, DestEdt);
FileListBox.AnchorToNeighbour(akTop, 5, FilesLabel);
AddAnyBtn.AnchorToNeighbour(akTop, 10, FileListBox);
AddImgBtn.AnchorToNeighbour(akTop, 10, FileListBox);
DeleteBtn.AnchorToNeighbour(akTop, 10, FileListBox);
ClearBtn.AnchorToNeighbour(akTop, 10, FileListBox);
AddImgBtn.AnchorToNeighbour(akLeft, 5 , AddAnyBtn);
DeleteBtn.AnchorToNeighbour(akLeft, 15, AddImgBtn);
ClearBtn.AnchorToNeighbour(akLeft, 5, DeleteBtn);
MessagesLabel.AnchorToNeighbour(akTop, 10, AddAnyBtn);
MsgMemo.AnchorToNeighbour(akTop, 5, MessagesLabel);
StartBtn.AnchorToNeighbour(akTop, 10, MsgMemo);
CloseBtn.AnchorToNeighbour(akTop, 10, MsgMemo);
CloseBtn.AnchorToNeighbour(akLeft, 5, StartBtn);
end;
procedure TGLazResForm.ResizeControls(Dummy: PtrInt);
var
CH, CW, LMargin, MinW, MinH, HeightLeft: Integer;
begin
CH := ClientHeight;
CW := ClientWidth;
LMargin := LrsLabel.Left;
MinW := ClearBtn.Left + ClearBtn.Width + 2 * LMargin;
DestEdt.Width := CW - (2 * LMargin) - DestEdt.ButtonWidth;
if (DestEdt.Width < MinW) then DestEdt.Width := MinW;
FileListBox.Width := CW - (2 * LMargin);
if (FileListBox.Width < MinW) then FileListBox.Width := MinW;
MsgMemo.Width := FileListBox.Width;
StartBtn.Left := CW - (StartBtn.Width + CloseBtn.Width + 5) - LMargin;
MinH := 532; //desing time value
if CH <= MinH then
begin
MsgMemo.Height := 138;// design time value;
Exit;
end;
HeightLeft := CH - MsgMemo.Top;
MsgMemo.Height := HeightLeft - StartBtn.Height - 10 - 10;
end;
procedure TGLazResForm.LoadWindowGeometry;
var
IniDir: String;
Ini: TIniFile;
L, T, W, H: LongInt;
begin
IniDir := ExtractFileDir(FIniFileName);
if not DirectoryExists(IniDir) then if not ForceDirectories(IniDir) then Exit;
try
Ini := TIniFile.Create(FIniFileName);
try
L := Ini.ReadInteger(scPosition, idLeft, Left);
T := Ini.ReadInteger(scPosition, idTop, Top);
W := Ini.ReadInteger(scPosition, idWidth, Width);
H := Ini.ReadInteger(scPosition, idHeight, Height);
SetBounds(L, T, W, H);
finally
Ini.Free;
end;
except
debugln('Error reading geometry from "',FIniFileName,'".');
end;
end;
procedure TGLazResForm.SaveWindowGeometry;
var
IniDir: String;
Ini: TIniFile;
begin
IniDir := ExtractFileDir(FIniFileName);
if not DirectoryExists(IniDir) then if not ForceDirectories(IniDir) then
begin
debugln('Unable to create config file "',FIniFileName,'".');
Exit;
end;
try
Ini := TIniFile.Create(FIniFileName);
try
Ini.CacheUpdates := True;
Ini.WriteInteger(scPosition, idLeft, Left);
Ini.WriteInteger(scPosition, idTop, Top);
Ini.WriteInteger(scPosition, idWidth, Width);
Ini.WriteInteger(scPosition, idHeight, Height);
finally
Ini.Free;
end;
except
debugln('Error saving geometry to "',FIniFileName,'".');
end;
end;
procedure TGLazResForm.MaybeEnableButtons;
begin
StartBtn.Enabled := (DestEdt.FileName <> '') and
(FileListBox.Count > 0);
DeleteBtn.Enabled := (FileListBox.Count > 0);
ClearBtn.Enabled := (FileListBox.Count > 0);
end;
// ************** LRS Creating related procedures ***************** //
procedure TGLazResForm.AddFiles(Names: TStrings);
var
Index: Integer;
begin
for Index := 0 to Names.Count - 1 do
begin
FileListBox.Items.Add(Names[Index]);
end;
MaybeEnableButtons;
end;
procedure TGLazResForm.ConvertFormToText(Stream: TMemoryStream);
var TextStream: TMemoryStream;
begin
try
try
TextStream:=TMemoryStream.Create;
FormDataToText(Stream,TextStream);
TextStream.Position:=0;
Stream.Clear;
Stream.CopyFrom(TextStream,TextStream.Size);
Stream.Position:=0;
except
on E: Exception do begin
debugln(Format(ErrConvertToText,[E.Message]));
end;
end;
finally
TextStream.Free;
end;
end;
procedure TGLazResForm.CreateLazarusResourceFile;
var
FileCount, Index:integer;
S:string;
ResFileStream, BinFileStream: TFileStream;
ResMemStream, BinMemStream: TMemoryStream;
ResourceFilename, FullResourceFilename, BinFilename, BinExt, ResourceName, ResourceType,
ExpS: String;
begin
FileCount := FileListBox.Count;
if FileCount = 0 then
Exit;
FullResourceFileName := ExpandFileNameUtf8(DestEdt.FileName);
ResourceFileName := ExtractFileName(FullResourceFileName);
ClearMessages;
// check that all resources exists and are not the destination file
for Index := 0 to FileCount-1 do
begin
S := FileListBox.Items[Index]; //FileListBox[Index];
if not FileExistsUTF8(S) then
begin
AddMessageFmt(ErrFileNotfound,[S]);
exit;
end;
ExpS:=ExpandFileNameUTF8(S);
if (CompareText(ExpS,FullResourceFilename)=0)
or (CompareFilenamesIgnoreCase(ExpandFileNameUTF8(S), FullResourceFilename) = 0) then
begin
AddMessageFmt(ErrFileIsResource,[S]);
exit;
end;
end;
try
AddMessageFmt(MsgCreatingLrs,[FullResourceFilename]);
ResFileStream:=TFileStream.Create(FullResourceFileName,fmCreate);
except
AddMessageFmt(ErrCreate,[ResourceFileName]);
exit;
end;
ResMemStream:=TMemoryStream.Create;
try
for Index := 0 to FileCount - 1 do
begin
BinFilename:=FileListBox.Items[Index];
AddMessageFmt(MsgProcessing,[BinFilename]);
try
BinFileStream:=TFileStream.Create(BinFilename, fmOpenRead);
BinMemStream:=TMemoryStream.Create;
try
BinMemStream.CopyFrom(BinFileStream, BinFileStream.Size);
BinMemStream.Position := 0;
BinExt := Utf8UpperCase(ExtractFileExt(BinFilename));
if (BinExt='.LFM') or (BinExt='.DFM') or (BinExt='.XFM')
then
begin
ResourceType:='FORMDATA';
ConvertFormToText(BinMemStream);
ResourceName:=FindLFMClassName(BinMemStream);
if ResourceName='' then
begin
AddMessageFmt(ErrNoResourceName,[BinFileName]);
exit;
end;
AddMessageFmt(MsgResourceNameType,[ResourceName,ResourceType]);
LFMtoLRSstream(BinMemStream,ResMemStream);
end
else
begin
ResourceType := copy(BinExt,2,length(BinExt)-1);
ResourceName := ExtractFileName(BinFilename);
ResourceName := copy(ResourceName, 1, Length(ResourceName) - Length(BinExt));
if (ResourceName = '') then
begin
AddMessageFmt(ErrNoResourceName,[BinFileName]);
exit;
end;
AddMessageFmt(MsgResourceNameType,[ResourceName,ResourceType]);
BinaryToLazarusResourceCode(BinMemStream, ResMemStream ,ResourceName, ResourceType);
end;
finally
BinFileStream.Free;
BinMemStream.Free;
end;
except
AddMessageFmt(ErrRead,[BinfileName]);
exit;
end;
end;
ResMemStream.Position := 0;
ResFileStream.CopyFrom(ResMemStream, ResMemStream.Size);
AddMessageFmt(MsgSuccess,[FileCount]);
finally
ResMemStream.Free;
ResFileStream.Free;
end;
end;
// **************** User interaction **************** //
procedure TGLazResForm.AddMessage(const Msg: String);
begin
MsgMemo.Lines.Add(Msg);
end;
procedure TGLazResForm.AddMessageFmt(const Msg: String; Args: array of const);
begin
MsgMemo.Lines.Add(Format(Msg, Args));
end;
procedure TGLazResForm.ClearMessages;
begin
MsgMemo.Lines.Clear;
end;
end.