New PoChecker IDE package from Bart Broersma

git-svn-id: trunk@34404 -
This commit is contained in:
juha 2011-12-25 13:15:33 +00:00
parent 3b7d2b72f2
commit 01233f98fe
11 changed files with 3538 additions and 0 deletions

10
.gitattributes vendored
View File

@ -1937,6 +1937,16 @@ components/plotfunction/regplotpanel.lrs svneol=native#text/pascal
components/plotfunction/regplotpanel.pp svneol=native#text/plain
components/plotfunction/tplotexpressionpanel.png -text svneol=unset#image/png
components/plotfunction/tplotfunctionpanel.png -text svneol=unset#image/png
components/pochecker/.directory svneol=native#text/plain
components/pochecker/README.txt svneol=native#text/plain
components/pochecker/pochecker.lpk svneol=native#text/plain
components/pochecker/pochecker.pas svneol=native#text/plain
components/pochecker/pocheckermain.lfm svneol=native#text/plain
components/pochecker/pocheckermain.pp svneol=native#text/plain
components/pochecker/pofamilies.pp svneol=native#text/plain
components/pochecker/resultdlg.lfm svneol=native#text/plain
components/pochecker/resultdlg.pp svneol=native#text/plain
components/pochecker/simplepofiles.pp svneol=native#text/plain
components/prettymessages/README.txt svneol=native#text/plain
components/prettymessages/hidefpchints.pas svneol=native#text/plain
components/prettymessages/prettymessages.lpk svneol=native#text/plain

View File

@ -0,0 +1,5 @@
[Dolphin]
AdditionalInfoV2=Details_Size,Details_Date,CustomizedDetails
Timestamp=2011,12,25,14,41,0
Version=2
ViewMode=1

View File

@ -0,0 +1,7 @@
This package checks the validity of translated PO files.
Original version made by Bart Broersma
ToDo:
- Find automatically all PO files belonging to the current project.
- Improve IDE integration

View File

@ -0,0 +1,75 @@
<?xml version="1.0"?>
<CONFIG>
<Package Version="4">
<Name Value="PoChecker"/>
<AddToProjectUsesSection Value="True"/>
<Author Value="Bart Broersma"/>
<CompilerOptions>
<Version Value="11"/>
<SearchPaths>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Other>
<CompilerMessages>
<UseMsgFile Value="True"/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Description Value="Check the validity of translated PO files.
"/>
<Version Major="1"/>
<Files Count="7">
<Item1>
<Filename Value="resultdlg.lfm"/>
<Type Value="LFM"/>
</Item1>
<Item2>
<Filename Value="resultdlg.pp"/>
<UnitName Value="ResultDlg"/>
</Item2>
<Item3>
<Filename Value="pofamilies.pp"/>
<UnitName Value="PoFamilies"/>
</Item3>
<Item4>
<Filename Value="pocheckermain.pp"/>
<HasRegisterProc Value="True"/>
<UnitName Value="pocheckermain"/>
</Item4>
<Item5>
<Filename Value="simplepofiles.pp"/>
<UnitName Value="SimplePoFiles"/>
</Item5>
<Item6>
<Filename Value="resultdlg.lfm"/>
<Type Value="LFM"/>
</Item6>
<Item7>
<Filename Value="pocheckermain.lfm"/>
<Type Value="LFM"/>
</Item7>
</Files>
<Type Value="RunAndDesignTime"/>
<RequiredPkgs Count="4">
<Item1>
<PackageName Value="FCL"/>
</Item1>
<Item2>
<PackageName Value="IDEIntf"/>
</Item2>
<Item3>
<PackageName Value="SynEdit"/>
</Item3>
<Item4>
<PackageName Value="LCL"/>
</Item4>
</RequiredPkgs>
<UsageOptions>
<UnitPath Value="$(PkgOutDir)"/>
</UsageOptions>
<PublishOptions>
<Version Value="2"/>
</PublishOptions>
</Package>
</CONFIG>

View File

@ -0,0 +1,21 @@
{ This file was automatically created by Lazarus. Do not edit!
This source is only used to compile and install the package.
}
unit PoChecker;
interface
uses
ResultDlg, PoFamilies, pocheckermain, SimplePoFiles, LazarusPackageIntf;
implementation
procedure Register;
begin
RegisterUnit('pocheckermain', @pocheckermain.Register);
end;
initialization
RegisterPackage('PoChecker', @Register);
end.

View File

@ -0,0 +1,149 @@
object PoCheckerForm: TPoCheckerForm
Left = 409
Height = 295
Top = 133
Width = 463
Caption = 'GUI Po-file checking tool'
ClientHeight = 295
ClientWidth = 463
OnCreate = FormCreate
OnDestroy = FormDestroy
LCLVersion = '0.9.31'
object TestListBox: TCheckListBox
Left = 168
Height = 163
Top = 44
Width = 288
Enabled = False
ItemHeight = 0
OnItemClick = TestListBoxItemClick
TabOrder = 0
end
object Label1: TLabel
Left = 168
Height = 15
Top = 24
Width = 105
Caption = 'Select test types'
Font.Style = [fsBold]
ParentColor = False
ParentFont = False
end
object OpenBtn: TBitBtn
Left = 8
Height = 79
Top = 44
Width = 144
Caption = '&Open a po-file'
Glyph.Data = {
36040000424D3604000000000000360000002800000010000000100000000100
2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00005E8E8D005D
8CBD005D8CBD005D8CBD005D8CBD005D8CBD005D8CBD005D8CBD005D8CBD005D
8CBD005D8CBD005D8CBD005D8CBD005E8E8D005B8900005B890000679AB06AB7
DAFF82CCEDFF82CCEDFF82CCEDFF82CCEDFF82CCEDFF82CCEDFF82CCEDFF82CC
EDFF82CCEDFF82CCEDFF83CDEEFF318DB9C9005E8E40005F8F000070A9A256AA
CEFF80CBEAFF7EC9E9FF7EC9E9FF7EC9E9FF7EC9E9FF7EC9E9FF7EC9E9FF7EC9
E9FF7EC9E9FF7EC9E9FF7EC9E9FF57AFD6D90066996E006699000074AD9D44A1
CBFF8AD3EFFF83CDEBFF83CDEBFF83CDEBFF83CDEBFF83CDEBFF83CDEBFF83CD
EBFF83CDEBFF83CDEBFF83CDEBFF81CDEBF2006FA8930071AB010076B29952B0
D7FF85D2EDFF89D2EEFF89D2EEFF89D2EEFF89D2EEFF89D2EEFF89D2EEFF89D2
EEFF89D2EEFF89D2EEFF89D2EEFF90D8F1FF228EC1AA0077B31D0079B69574CA
E8FF75CAE8FF90D8F2FF8FD7F1FF8FD7F1FF8FD7F1FF8FD7F1FF8FD7F1FF8FD7
F1FF8FD7F1FF8FD7F1FF8FD7F1FF91D8F2FF4FB1DAC2007FBD46007CBA928FDD
F4FF63C0E5FFA8EEFAFFA8EEFAFFA8EEFAFFA8EEFAFFA8EEFAFFA8EEFAFFA8EE
FAFFA8EEFAFFA8EEFAFFA8EEFAFFA8EEFAFF86D8EFDE0083C571007FBD8EA6EC
FCFF64C2E9FF4FB5E2FF4DB4E2FF4CB3E1FF4BB2E0FF49B1DFFF48B0DFFF47AE
DEFF45ADDDFF44ACDDFF46AEDFFF0084C6C70087CB810087CB610081C18BABF0
FEFFA4E9FCFFA2E7FBFF9FE5FAFF9CE3F8FF9AE1F7FF97DEF6FF94DCF4FF91D9
F3FF8ED7F1FF8BD4F0FF90D8F3FF0081C18B0087CA000087CB000083C488ADF1
FFFFA6EBFDFFA4E9FCFFA2E7FBFF9FE5FAFF9CE3F8FF9AE1F7FF97DEF6FF94DC
F4FF91D9F3FF8ED7F1FF93DAF4FF0083C4880083C4000085C8000085C785B0F4
FFFFADF1FFFFABF0FEFFA9EEFDFFA7ECFCFFA5EAFBFFA2E8FAFFA0E6F9FF9DE3
F8FF9AE1F7FF98DFF6FF99E0F7FF0085C7850085C7000085C7000087CA620087
CA830087CA830087CA830087CA830087CA830087CA83FEFEFDFFF5F5EEFFEBEB
DDFFFEC941FFF4B62EFF0087CA830087CA620086C9000086C9000087CA000087
CA000087CA000087CA000087CA000087CA000088CC2E0088CC810088CC810088
CC810088CC810088CC810088CC2E0087CA000086C9000086C900FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
}
OnClick = OpenBtnClick
TabOrder = 1
end
object RunBtn: TBitBtn
Left = 8
Height = 79
Top = 128
Width = 144
Caption = '&Run Selected Tests'
Enabled = False
Kind = bkOK
OnClick = RunBtnClick
TabOrder = 2
end
object StatusPanel: TPanel
Left = 0
Height = 48
Top = 247
Width = 463
Align = alBottom
BevelOuter = bvLowered
ClientHeight = 48
ClientWidth = 463
TabOrder = 3
object Label2: TLabel
Left = 8
Height = 15
Top = 8
Width = 74
Caption = 'Current Test:'
ParentColor = False
end
object Label3: TLabel
Left = 8
Height = 15
Top = 24
Width = 87
Caption = 'Current po-file:'
ParentColor = False
end
object CurTestLabel: TLabel
Left = 104
Height = 15
Top = 8
Width = 73
Caption = 'CurTestLabel'
ParentColor = False
end
object CurPoLabel: TLabel
Left = 104
Height = 15
Top = 24
Width = 65
Caption = 'CurPoLabel'
ParentColor = False
end
end
object NoErrLabel: TLabel
Left = 8
Height = 24
Top = 216
Width = 171
Caption = 'No errors found'
Font.Color = clRed
Font.Height = -19
Font.Style = [fsBold]
ParentColor = False
ParentFont = False
end
object OpenDialog: TOpenDialog
Filter = 'po-files (*.po)|*.po|all files|*'
Options = [ofFileMustExist, ofEnableSizing, ofViewDetail]
left = 40
top = 512
end
end

View File

@ -0,0 +1,335 @@
{
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.
}
// Original version made by Bart Broersma
unit pocheckermain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, SynEdit, Forms, Controls, Graphics, Dialogs,
StdCtrls, LCLProc, CheckLst, Buttons, ExtCtrls, IDEIntf, MenuIntf,
SimplePoFiles, PoFamilies, ResultDlg;
type
{ TPoCheckerForm }
TPoCheckerForm = class(TForm)
Label2: TLabel;
Label3: TLabel;
CurTestLabel: TLabel;
CurPoLabel: TLabel;
NoErrLabel: TLabel;
StatusPanel: TPanel;
RunBtn: TBitBtn;
OpenBtn: TBitBtn;
Button3: TButton;
Label1: TLabel;
OpenDialog: TOpenDialog;
TestListBox: TCheckListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure OpenBtnClick(Sender: TObject);
procedure RunBtnClick(Sender: TObject);
procedure TestListBoxItemClick(Sender: TObject; Index: integer);
private
PoFamily: TPoFamily;
FChoosenMasterName: String;
FChoosenChildName: String;
procedure OnTestStart(const ATestName, APoFileName: String);
procedure OnTestEnd(const ATestName: String; const ErrorCount: Integer);
procedure FillTestListBox;
function GetOptionsFromListBox: TPoTestOptions;
procedure ShowError(const Msg: String);
function TrySelectFile: Boolean;
procedure RunSelectedTests;
procedure ClearAndDisableStatusPanel;
public
end;
resourcestring
rsPoChecker = 'PO File Checker';
sSelectAllTests = 'Select all tests';
sUnSelectAllTests = 'Unselect all tests';
sCannotFindMaster = 'Cannot find master po file:'^m'%s'^m'for selected file'^m'%s';
sNotAProperFileName = 'Selected filename'^m'%s'^m'does not seem to be a proper name for a po-file';
sErrorOnCreate = 'Error creating an instance of TPoFamily:'^m'%s';
sErrorOnCleanup = 'An unrecoverable error occurred'^m'%s'^m'Please close the program';
sTotalErrors = 'Total errors found: %d';
//sNoErrorsFound = 'No errors found.';
sNoTestSelected = 'There are no tests selected.';
var
PoCheckerForm: TPoCheckerForm;
procedure Register;
implementation
{$R *.lfm}
procedure ShowPoCheckerForm();
begin
if not Assigned(PoCheckerForm) then
PoCheckerForm := TPoCheckerForm.Create(Application);
PoCheckerForm.Show;
end;
{ TPoCheckerForm }
procedure TPoCheckerForm.FormCreate(Sender: TObject);
begin
FillTestListBox;
ClearAndDisableStatusPanel;
NoErrLabel.Visible := False;
end;
procedure TPoCheckerForm.FormDestroy(Sender: TObject);
begin
if Assigned(PoFamily) then PoFamily.Free;
end;
procedure TPoCheckerForm.OpenBtnClick(Sender: TObject);
begin
if TrySelectFile then
begin
RunBtn.Enabled := True;
TestListBox.Enabled := True;
end
else
begin
RunBtn.Enabled := False;
TestListBox.Enabled := False;
end;
end;
procedure TPoCheckerForm.RunBtnClick(Sender: TObject);
begin
RunSelectedTests;
end;
procedure TPoCheckerForm.TestListBoxItemClick(Sender: TObject; Index: integer);
var
Check: Boolean;
i: Integer;
begin
if (Index = TestListBox.Count - 1) then
begin//Run All Test checkbox
Check := TestListBox.Checked[Index];
for i := 0 to TestListBox.Count - 2 do TestListBox.Checked[i] := Check;
if Check then
TestListBox.Items[Index] := sUnSelectAllTests
else
TestListBox.Items[Index] := sSelectAllTests;
end;
end;
procedure TPoCheckerForm.OnTestStart(const ATestName, APoFileName: String);
begin
//debugln('OnTestStart: ATestName = "',AtestName,'" APoFileName = "',APoFileName);
CurTestLabel.Caption := ATestName;
CurPoLabel.Caption := APoFileName;
Application.ProcessMessages;
end;
procedure TPoCheckerForm.OnTestEnd(const ATestName: String; const ErrorCount: Integer);
begin
//CurTestLabel.Caption := '';
//CurPoLabel.Caption := '';
debugln('OnTestEnd [',ATestName,']: ErrorCount = ',DbgS(ErrorCount));
//Application.ProcessMessages;
end;
procedure TPoCheckerForm.FillTestListBox;
var
Opt: TPoTestOption;
begin
for Opt := Low(PoTestOptionNames) to High(PoTestOptionNames) do
begin
TestListBox.Items.Add(PoTestOptionNames[Opt]);
end;
TestListBox.Items.Add(sSelectAllTests);
end;
function TPoCheckerForm.GetOptionsFromListBox: TPoTestOptions;
var
Opt: TPoTestOption;
Index: Integer;
begin
Result := [];
for Opt := Low(TpoTestOption) to High(TPoTestOption) do
begin
Index := Ord(Opt);
if (Index < TestListBox.Count) then
begin
if TestListBox.Checked[Index] then Result := Result + [Opt];
end;
end;
end;
procedure TPoCheckerForm.ShowError(const Msg: String);
begin
MessageDlg('GPoCheck', Msg, mtError, [mbOk], 0);
end;
function TPoCheckerForm.TrySelectFile: Boolean;
var
Fn: String;
ShortFn: String;
OK: Boolean;
begin
NoErrLabel.Visible := False;
OK := False;
if OpenDialog.Execute then
begin
Fn := OpenDialog.FileName;
ShortFn := ExtractFileName(Fn);
if IsMasterPoName(Fn) then
begin
FChoosenMasterName := Fn;
FChoosenChildName := '';
end
else
begin //not a mastername, may be a child
FChoosenChildName := Fn;
FChoosenMasterName := ExtractMasterNameFromChildName(Fn);
if (FChoosenMasterName = '') then
begin
FChoosenMasterName := '';
FChoosenChildName := '';
ShowError(Format(sNotAProperFileName,[ShortFn]));
end
else if not FileExistsUtf8(FChoosenMasterName) then
begin
FChoosenMasterName := '';
FChoosenChildName := '';
ShowError(Format(sCannotFindMaster,[ShortFn]));
end;
end;
OK := (FChoosenMasterName <> '');
if OK then
begin
if Assigned(PoFamily) then PoFamily.Free;
try
PoFamily := TPoFamily.Create(FChoosenMasterName, FChoosenChildName);
PoFamily.OnTestStart := @OnTestStart;
PoFamily.OnTestEnd := @OnTestEnd;
except
on E: Exception do
begin
OK := False;
ShowError(Format(sErrorOnCreate,[E.Message]));
if Assigned(PoFamily) then
begin
try
PoFamily.Free;
except
on E: Exception do
begin
ShowError(Format(sErrorOnCleanUp,[E.Message]));
end;
end;
end;
end;
end;
end;
end;
Result := OK;
end;
procedure TPoCheckerForm.RunSelectedTests;
var
Options: TPoTestOptions;
ErrorCount: Integer;
SL: TStrings;
ResultDlg: TResultDlgForm;
begin
Options := GetOptionsFromListBox;
if (Options = []) then
begin
ShowError(sNoTestSelected);
Exit;
end;
NoErrLabel.Visible := False;
Application.ProcessMessages;
SL := TStringList.Create;
try
StatusPanel.Enabled := True;
if (not (ptoFindAllChilds in Options)) and Assigned(PoFamily.Child) and
(PoFamily.ChildName <> FChoosenChildName) then PoFamily.ChildName := FChoosenChildName;
PoFamily.RunTests(Options, ErrorCount, SL);
if (ErrorCount > 0) then
begin
debugln('RunSelectedTests: ',Format(sTotalErrors,[ErrorCount]));
SL.Add(Format(sTotalErrors,[ErrorCount]));
ResultDlg := TResultDlgForm.Create(Nil);
try
ResultDlg.Log.Assign(SL);
FreeAndNil(SL); //No need to keep 2 copies of this data
ResultDlg.ShowModal;
finally
ResultDlg.Free;
end;
end
else
begin//no errors
NoErrLabel.Visible := True;
end;
finally
if Assigned(SL) then
SL.Free;
ClearAndDisableStatusPanel;
end;
end;
procedure TPoCheckerForm.ClearAndDisableStatusPanel;
begin
CurTestLabel.Caption := '';
CurPoLabel.Caption := '';
StatusPanel.Enabled := False;
end;
function SameItem(Item1, Item2: TPoFileItem): Boolean;
begin
Result := (Item1.Identifier = Item2.Identifier) and
(Item1.Original = Item2.Original) and
(Item1.Context = Item2.Context) and
(Item1.Flags = Item2.Flags) and
(Item1.PreviousID = Item2.PreviousID) and
(Item1.Translation = Item2.Translation);
end;
procedure IDEMenuClicked(Sender: TObject);
begin
ShowPoCheckerForm;
end;
procedure Register;
begin
RegisterIDEMenuCommand(itmSecondaryTools, 'mnuPoChecker', rsPoChecker, nil, @IDEMenuClicked);
end;
end.

View File

@ -0,0 +1,703 @@
unit PoFamilies;
{ $define DebugSimplePoFiles}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LCLProc, FileUtil, StringHashList,
{LConvEncoding}
//{$IFDEF UNIX}{$IFNDEF DisableCWString}, cwstring{$ENDIF}{$ENDIF},
SimplePoFiles;
Type
TPoTestOption = (ptoCheckNrOfItems, ptoCheckFormatArgs, ptoCheckMissingIdentifiers,
ptoCheckMismatchedOriginals, ptoCheckDuplicateOriginals,
ptoFindAllChilds);
TPoTestOptions = Set of TPoTestOption;
const
optRunAllTests: TPoTestOptions = [];
optRunAllTestsOnAllChilds: TPoTestOptions = [];
PoTestOptionNames: array[TPoTestOption] of String = ('Check number of items', 'Check for incompatible format arguments',
'Check missing identifiers','Check for mismatches in untranslated strings',
'Check for duplicate untranslated values',
'Find all translated po-files');
Type
{ TPoFamily }
TTestStartEvent = procedure(const ATestName, APoFileName: String) of object;
TTestEndEvent = procedure(const ATestName: String; const ErrorCount: Integer) of object;
TPoFamily = class
private
FMaster: TSimplePoFile;
FChild: TSimplePoFile;
FMasterName: String;
FChildName: String;
FOnTestStart: TTestStartEvent;
FOnTestEnd: TTestEndEvent;
procedure SetChildName(AValue: String);
procedure SetMasterName(AValue: String);
function GetShortMasterName: String;
function GetShortChildName: String;
protected
procedure DoTestStart(const ATestName, APoFileName: String);
procedure DoTestEnd(const ATestName: String; const ErrorCount: Integer);
public
constructor Create;
constructor Create(const MasterName: String);
constructor Create(const AMasterName, AChildName: String);
destructor Destroy; override;
protected
procedure CheckNrOfItems(out ErrorCount: Integer; ErrorLog: TStrings);
procedure CheckFormatArgs(out ErrorCount: Integer; ErrorLog: TStrings);
procedure CheckMissingIdentifiers(out ErrorCount: Integer; ErrorLog: TStrings);
procedure CheckMismatchedOriginals(out ErrorCount: Integer; ErrorLog: TStrings);
procedure CheckDuplicateOriginals(out ErrorCount: Integer; ErrorLog: TStrings);
public
procedure RunTests(const Options: TPoTestOptions; out ErrorCount: Integer; ErrorLog: TStrings);
property Master: TSimplePoFile read FMaster;
property Child: TSimplePoFile read FChild;
property MasterName: String read FMasterName write SetMasterName;
property ChildName: String read FChildName write SetChildName;
property ShortMasterName: String read GetShortMasterName;
property ShortChildName: String read GetShortChildName;
property OnTestStart: TTestStartEvent read FOnTestStart write FOnTestStart;
property OnTestEnd: TTestEndEvent read FOnTestEnd write FOnTestEnd;
end;
function ExtractFormatArgs(S: String): String;
function IsMasterPoName(const Fn: String): Boolean;
function ExtractMasterNameFromChildName(const AChildName: String): String;
function FindAllTranslatedPoFiles(const Filename: string): TStringList;
implementation
const
NoError = 0;
sCommentIdentifier = '#: ';
//sCharSetIdentifier = '"Content-Type: text/plain; charset=';
sMsgID = 'msgid "';
sMsgStr = 'msgstr "';
//sMsgCtxt = 'msgctxt "';
//sFlags = '#, ';
//sPrevMsgID = '#| msgid "';
//sPrevStr = '#| "';
Divider = '--------------------------------------------------';
sOriginal = 'Original';
sTranslation = 'Translation';
sErrorsByTest = 'Errors reported by %s for:';
sCheckFormatArgs = 'CheckFormatArgs';
sCheckMissingIdentifiers = 'CheckMissingIdentifiers';
sCheckNrOfItems = 'CheckNrOfItems';
sCheckMismatchedOriginals = 'CheckMismatchedOriginals';
sCheckDuplicateOriginals = 'CheckDiplicateOriginals';
sIncompatibleFormatArgs = '[Line: %d] Incompatible format() arguments for:' ;
sNrErrorsFound = 'Found %d errors.';
sLineInFileName = '[Line %d] in %s:';
sIdentifierNotFoundIn = 'Identifier [%s] not found in %s';
sMissingMasterIdentifier = 'Identifier [%s] found in %s, but it does not exist in %s';
sLineNr = '[Line: %d]';
sFormatArgsID = '%s %s';
sFormatArgsValues = '%s%s" (= %s)';
sNrOfItemsMisMatch = 'Mismatch in number of items for master and child';
sNrOfItemsMismatchM = '%s: %d items';
sNrOfItemsMismatchC = '%s: %d items';
sMismatchOriginalsID = '%s';
sMismatchOriginalsM = '%s: %s';
sMismatchOriginalsC = '%s: %s';
sDuplicateOriginals = 'This resourcestring:';
sDuplicateIdentifier = '#: %s';
sDuplicateOriginal = 'msgid "%s"';
sDuplicateContext = 'msgctxt "%s"';
sDuplicateOriginals2 = 'has the same value as idenftifier %s at line %d';
sDuplicateOriginals3 = 'For this entry it is recommended to set: msgctxt="%s"';
//Helper functions
function ExtractFormatArgs(S: String): String;
const
FormatSpecs = ['D','E','F','G','N','M','P','S','X'];
var
i,p: Integer;
InFormat: Boolean;
NewStr: String;
c: Char;
begin
SetLength(NewStr, Length(S));
InFormat := False;
p := 0;
for i := 1 to length(S) do
begin
c := S[i];
if (c = '%') then InFormat := not InFormat;
//debugln('i = ',dbgs(i),' c = ',c,' InFormat = ',dbgs(informat));
if InFormat and (UpCase(c) in (FormatSpecs+['%'])) then
begin
begin
Inc(p);
NewStr[p] := c;
end;
end
else
begin
if (c = '%') and (i > 1) and (S[i-1] = '%') and (p > 0) and (NewStr[p] = '%') then
begin//2 consecutive % means a literal % and is not a format specifier
//debugln('p = ',dbgs(p), 'i = ',dbgs(i));
NewStr[p] := '#';
Dec(p);
end;
end;
if InFormat and (Upcase(c) in FormatSpecs) then InFormat := False;
end;
SetLength(NewStr, p);
Result := NewStr;
end;
function IsMasterPoName(const Fn: String): Boolean;
//Returns True if Fn is like '[Path/To/]somename.po'
var
Ext: String;
S: String;
begin
S := ExtractFileName(Fn);
Ext := ExtractFileExt(S);
S := Copy(S, 1, Length(S) - Length(Ext));
Result := (Length(S) > 0) and
(CompareText(Ext, ExtensionSeparator + 'po') = 0) and
(Pos(ExtensionSeparator, S) = 0);
end;
function ExtractMasterNameFromChildName(const AChildName: String): String;
{
Pre condition: AChildName is like: somename.some_language_specifier.po
Post condition: Result = somename.po
}
var
Ext: String;
EndSep: Set of Char;
Len: Integer;
begin
EndSep := AllowDirectorySeparators + AllowDriveSeparators + [ExtensionSeparator];
Ext := ExtractFileExt(AChildName);
Result := Copy(AChildName, 1, Length(AChildName) - Length(Ext));
Len := Length(Result);
While (Len > 0) and (not (Result[Len] in EndSep)) do Dec(Len);
//debugln('Len = ',DbgS(Len));
//debugln('Length(Result) = ',DbgS(Length(result)));
//if Len > 0 then debugln('Result[Len] = ',Result[len]);
if (Len > 1) and (Len < Length(Result)) and (Result[Len] = ExtensionSeparator) then
Result := Copy(Result, 1, Len - 1) + Ext
else
Result := '';
end;
function FindAllTranslatedPoFiles(const Filename: string): TStringList;
var
Path: String;
Name: String;
NameOnly: String;
Ext: String;
FileInfo: TSearchRec;
CurExt: String;
begin
Result := TStringList.Create;
Path := ExtractFilePath(Filename);
Name := ExtractFilename(Filename);
Ext := ExtractFileExt(Filename);
NameOnly := LeftStr(Name,length(Name)-length(Ext));
if FindFirstUTF8(Path+GetAllFilesMask,faAnyFile,FileInfo)=0 then
begin
repeat
if (FileInfo.Name = '.') or (FileInfo.Name = '..') or (FileInfo.Name = '')
or (CompareFilenames(FileInfo.Name,Name) = 0) then continue;
CurExt:=ExtractFileExt(FileInfo.Name);
if (CompareFilenames(CurExt,'.po') <> 0)
or (CompareFilenames(LeftStr(FileInfo.Name,length(NameOnly)),NameOnly) <> 0)
then
continue;
Result.Add(Path+FileInfo.Name);
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
function CompareFormatArgs(S1, S2: String): Boolean;
begin
Result := CompareText(ExtractFormatArgs(S1), ExtractFormatArgs(S2)) = 0;
end;
{ TPoFamily }
procedure TPoFamily.SetMasterName(AValue: String);
begin
if FMasterName = AValue then Exit;
FMaster.Free;
FMaster := nil;
FMasterName := '';
if (AValue <> '') then FMaster := TSimplePoFile.Create(AValue{, True});
FMasterName := AValue;
end;
function TPoFamily.GetShortMasterName: String;
begin
Result := ExtractFileName(FMasterName);
end;
function TPoFamily.GetShortChildName: String;
begin
Result := ExtractFileName(FChildName);
end;
procedure TPoFamily.DoTestStart(const ATestName, APoFileName: String);
begin
if Assigned(FOnTestStart) then FOnTestStart(ATestName, APoFileName);
end;
procedure TPoFamily.DoTestEnd(const ATestName: String; const ErrorCount: Integer);
begin
if Assigned(FOnTestEnd) then FOnTestEnd(ATestName, ErrorCount);
end;
procedure TPoFamily.SetChildName(AValue: String);
begin
if FChildName = AValue then Exit;
FChild.Free;
FChild := nil;
FChildName := '';
if (AValue <> '') then FChild := TSimplePoFile.Create(AValue{, True});
FChildName := AValue;
end;
constructor TPoFamily.Create;
begin
Create('','');
end;
constructor TPoFamily.Create(const MasterName: String);
begin
Create(MasterName, '');
end;
constructor TPoFamily.Create(const AMasterName, AChildName: String);
begin
if (AMasterName <> '') then
begin
FMaster := TSimplePoFile.Create(AMasterName, True);
FMasterName := AMasterName;
//debugln('TPoFamily.Create: created ',FMasterName);
end;
if (AChildName <> '') then
begin
FChild := TSimplePoFile.Create(AChildName, True);
FChildName := AChildName;
//debugln('TPoFamily.Create: created ',FChildName);
end;
end;
destructor TPoFamily.Destroy;
begin
if Assigned(FMaster) then FMaster.Free;
if Assigned(FChild) then FChild.Free;
inherited Destroy;
end;
procedure TPoFamily.CheckNrOfItems(out ErrorCount: Integer; ErrorLog: TStrings);
begin
//debugln('TPoFamily.CheckNrOfItems');
DoTestStart(PoTestOptionNames[ptoCheckNrOfItems], ShortChildName);
if (FMaster.Count <> FChild.Count) then
begin
ErrorCount := 1;
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckNrOfItems]));
ErrorLog.Add(ShortChildName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add(sNrOfItemsMismatch);
ErrorLog.Add(Format(sNrOfItemsMismatchM,[ShortMasterName,FMaster.Count]));
ErrorLog.Add(Format(sNrOfItemsMismatchC,[ShortChildName,FChild.Count]));
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add('');
end
else ErrorCount := NoError;
DoTestEnd(PoTestOptionNames[ptoCheckNrOfItems], ErrorCount);
//debugln('TPoFamily.CheckNrOfItemsMismatch: ',Dbgs(ErrorCount),' Errors');
end;
procedure TPoFamily.CheckFormatArgs(out ErrorCount: Integer; ErrorLog: TStrings);
var
i: Integer;
CPoItem: TPOFileItem;
begin
//debugln('TPoFamily.CheckFormatArgs');
DoTestStart(PoTestOptionNames[ptoCheckFormatArgs], ShortChildName);
ErrorCount := NoError;
//for i := 0 to FMaster.Count - 1 do
for i := 0 to FChild.Count - 1 do
begin
//debugln(' i = ',DbgS(i));
//MPoItem := FMaster.PoItems[i];
CPoItem := FChild.PoItems[i];
//CPoItem := FChild.FindPoItem(MPoItem.Identifier);
if Assigned(CPoItem) then
begin
if (Pos('%', CPoItem.Translation) > 0) and not CompareFormatArgs(CPoItem.Original, CPoItem.Translation) then
begin
if (ErrorCount = 0) then
begin
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckFormatArgs]));
ErrorLog.Add(ShortChildName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
end;
Inc(ErrorCount);
ErrorLog.Add(Format(sIncompatibleFormatArgs,[CPoItem.LineNr]));
ErrorLog.Add(Format(sFormatArgsID,[sCommentIdentifier, CPoItem.Identifier]));
ErrorLog.Add(Format(sFormatArgsValues,[sMsgID,CPoItem.Original,sOriginal]));
ErrorLog.Add(Format(sFormatArgsValues,[sMsgStr,CPoItem.Translation,sTranslation]));
ErrorLog.Add('');
end;
end;
end;
if (ErrorCount > 0) then
begin
ErrorLog.Add(Format(sNrErrorsFound,[ErrorCount]));
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add('');
end;
DoTestEnd(PoTestOptionNames[ptoCheckFormatArgs], ErrorCount);
//debugln('TPoFamily.CheckIncompatibleFormatArgs: ',Dbgs(ErrorCount),' Errors');
end;
procedure TPoFamily.CheckMissingIdentifiers(out ErrorCount: Integer;
ErrorLog: TStrings);
var
i: Integer;
MPoItem, CPoItem: TPOFileItem;
begin
//debugln('TPoFamily.CheckMissingIdentifiers');
DoTestStart(PoTestOptionNames[ptoCheckMissingIdentifiers], ShortChildName);
ErrorCount := NoError;
for i := 0 to FMaster.Count - 1 do
begin
MPoItem := FMaster.PoItems[i];
if Assigned(MPoItem) and (MPoItem.Identifier <> '') then
begin
CPoItem := FChild.FindPoItem(MPoItem.Identifier);
if not Assigned(CPoItem) then
begin
if (ErrorCount = 0) then
begin
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckMissingIdentifiers]));
ErrorLog.Add(ShortChildName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
end;
Inc(ErrorCount);
ErrorLog.Add(Format(sLineInFileName,
[MPoItem.LineNr,ShortMasterName]));
ErrorLog.Add(Format(sIdentifierNotFoundIn,
[MPoItem.Identifier,ShortChildName]));
ErrorLog.Add('');
end;
end;
end;
//Now reverse the search
for i := 0 to FChild.Count - 1 do
begin
CPoItem := FChild.PoItems[i];
if Assigned(CPoItem) and (CPoItem.Identifier <> '') then
begin
MPoItem := FMaster.FindPoItem(CPoItem.Identifier);
if not Assigned(MPoItem) then
begin
if (ErrorCount = 0) then
begin
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckMissingIdentifiers]));
ErrorLog.Add(ShortChildName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
end;
Inc(ErrorCount);
ErrorLog.Add(Format(sLineNr,
[CPoItem.LineNr]));
ErrorLog.Add(Format(sMissingMasterIdentifier,
[CPoItem.Identifier,ShortChildName,ShortMasterName]));
ErrorLog.Add('');
end;
end;
end;
if (ErrorCount > 0) then
begin
ErrorLog.Add(Format(sNrErrorsFound,[ErrorCount]));
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add('');
end;
DoTestEnd(PoTestOptionNames[ptoCheckMissingIdentifiers], ErrorCount);
//debugln('TPoFamily.CheckMissingIdentifiers: ',Dbgs(ErrorCount),' Errors');
end;
procedure TPoFamily.CheckMismatchedOriginals(out ErrorCount: Integer;
ErrorLog: TStrings);
var
i: Integer;
MPoItem, CPoItem: TPOFileItem;
begin
//debugln('TPoFamily.CheckMismatchedOriginals');
DoTestStart(PoTestOptionNames[ptoCheckMismatchedOriginals], ShortChildName);
ErrorCount := NoError;
for i := 0 to FMaster.Count - 1 do
begin
MPoItem := FMaster.PoItems[i];
CPoItem := FChild.FindPoItem(MpoItem.Identifier);
if Assigned(CPoItem) then
begin
if (MPoItem.Original <> CPoItem.Original) then
begin
if (ErrorCount = 0) then
begin
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckMismatchedOriginals]));
ErrorLog.Add(ShortChildName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
end;
Inc(ErrorCount);
ErrorLog.Add(Format(sLineInFileName,[CpoItem.LineNr, ShortChildName]));
ErrorLog.Add(Format(sMismatchOriginalsID,[CPoItem.Identifier]));
ErrorLog.Add(Format(sMismatchOriginalsM,[ShortMasterName,MPoItem.Original]));
ErrorLog.Add(Format(sMismatchOriginalsC,[ShortChildName, CPoItem.Original]));
ErrorLog.Add('');
end;
end;
end;
if (ErrorCount > 0) then
begin
ErrorLog.Add(Format(sNrErrorsFound,[ErrorCount]));
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add('');
end;
DoTestEnd(PoTestOptionNames[ptoCheckMismatchedOriginals], ErrorCount);
//debugln('TPoFamily.CheckMismatchedOriginals: ',Dbgs(ErrorCount),' Errors');
end;
procedure TPoFamily.CheckDuplicateOriginals(out ErrorCount: Integer;
ErrorLog: TStrings);
var
i: Integer;
PoItem, Dup: TPOFileItem;
begin
//debugln('TPoFamily.CheckMismatchedOriginals');
DoTestStart(PoTestOptionNames[ptoCheckDuplicateOriginals], ShortMasterName);
ErrorCount := NoError;
for i := FMaster.Count - 1 downto 0 do
begin
PoItem := FMaster.PoItems[i];
Dup := FMaster.OriginalToItem(PoItem.Original);
if Assigned(Dup) and (Dup.Identifier <> PoItem.Identifier) and (Dup.Context = '') then
begin
if (ErrorCount = 0) then
begin
ErrorLog.Add(Divider);
ErrorLog.Add(Format(sErrorsByTest,[sCheckDuplicateOriginals]));
ErrorLog.Add(ShortMasterName);
ErrorLog.Add(Divider);
ErrorLog.Add('');
end;
Inc(ErrorCount);
ErrorLog.Add(Format(sLineNr,[PoItem.LineNr]));
ErrorLog.Add(sDuplicateOriginals);
ErrorLog.Add(Format(sDuplicateIdentifier,[PoItem.Identifier]));
ErrorLog.Add(Format(sDuplicateOriginal,[PoItem.Original]));
ErrorLog.Add(Format(sDuplicateContext,[PoItem.Context]));
ErrorLog.Add(Format(sDuplicateOriginals2,[Dup.Identifier,Dup.LineNr]));
ErrorLog.Add(Format(sDuplicateOriginals3,[PoItem.Identifier]));
ErrorLog.Add('');
end;
end;
if (ErrorCount > 0) then
begin
ErrorLog.Add(Format(sNrErrorsFound,[ErrorCount]));
ErrorLog.Add(Divider);
ErrorLog.Add('');
ErrorLog.Add('');
end;
DoTestEnd(PoTestOptionNames[ptoCheckDuplicateOriginals], ErrorCount);
//debugln('TPoFamily.CheckDuplicateOriginals: ',Dbgs(ErrorCount),' Errors');
end;
{
procedure TPoFamily.RunTests(const Options: TPoTestOptions; out
Pre conditions:
* Master and a matching Child must be assigned at start ot testing
* If a Child is assigned it must be child of Master
}
procedure TPoFamily.RunTests(const Options: TPoTestOptions; out
ErrorCount: Integer; ErrorLog: TStrings);
var
SL: TStringList;
CurrErrCnt: Integer;
i: Integer;
CurrChildName: String;
S: String;
begin
SL := nil;
ErrorCount := NoError;
if (not Assigned(FMaster)) and (not Assigned(FChild)) then
begin
{$ifdef DebugSimplePoFiles}
debugln('TPoFamily.RunTests: Both master and child are unassigned.');
{$endif}
Exit;
end;
if not Assigned(FMaster) then
begin
S := ExtractMasterNameFromChildName(FChildName);
if (S <> '') and FileExistsUtf8(S) then
begin
SetMasterName(S);
end
else
begin
{$ifdef DebugSimplePoFiles}
Debugln('TPoFamily.RunTests: Cannot find master for ',ShortChildName);
{$endif}
Exit;
end
end;
if not Assigned(FChild) and ([ptoFindAllChilds, ptoCheckDuplicateOriginals] * Options = []) then
begin
{$ifdef DebugSimplePoFiles}
Debugln('TPoFamily.RunTests: no child assigned for ',ShortMasterName);
{$endif}
Exit;
end;
if (ptoFindAllChilds in Options) then
begin
SL := FindAllTranslatedPoFiles(FMasterName);
//We want current Child (if currently assigned) at index 0
if Assigned(FChild) then
begin
for i := 0 to SL.Count - 1 do
begin
if (CompareFileNames(Sl.Strings[i], FChildName) = 0) then
begin
if (i <> 0) then SL.Exchange(i,0);
Break;
end;
end;
end;
end
else
begin
SL := TStringList.Create;
Sl.Add(FChildName);
end;
// for i := 0 to sl.count - 1 do debugln(extractfilename(sl.strings[i]));
try
//First run checks that are Master-only
if (ptoCheckDuplicateOriginals in Options) then
begin
CheckDuplicateOriginals(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
//then iterate all Children
for i := 0 to SL.Count - 1 do
begin
CurrChildName := SL.Strings[i];
//debugln('TPoFamily.RunTests: setting ChildName to ',CurrChildName);
SetChildName(CurrChildName);
if (ptoCheckNrOfItems in Options) then
begin
CheckNrOfItems(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
if (ptoCheckFormatArgs in Options) then
begin
CheckFormatArgs(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
if (ptoCheckMissingIdentifiers in Options) then
begin
CheckMissingIdentifiers(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
if (ptoCheckMismatchedOriginals in Options) then
begin
CheckMismatchedOriginals(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
{
if (pto in Options) then
begin
Check(CurrErrCnt, ErrorLog);
ErrorCount := CurrErrCnt + ErrorCount;
end;
}
end;
finally
SL.Free;
end;
//debugln('TPoFamilyRunTests: ErrorCount = ',DbgS(ErrorCount));
end;
procedure InitTestOptions;
var
Index: TPoTestOption;
begin
for Index := Low(TPoTestOption) to High(TPotestOption) do optRunAllTestsOnAllChilds := optRunAllTestsOnAllChilds + [Index];
optRunAllTests := optRunAllTestsOnAllChilds - [ptoFindAllChilds];
end;
Initialization
InitTestOptions;
end.

View File

@ -0,0 +1,620 @@
object ResultDlgForm: TResultDlgForm
Left = 284
Height = 635
Top = 108
Width = 742
ActiveControl = CopyBtn
Caption = 'Results'
ClientHeight = 635
ClientWidth = 742
KeyPreview = True
OnClose = FormClose
OnCreate = FormCreate
OnDestroy = FormDestroy
OnKeyDown = FormKeyDown
OnShow = FormShow
LCLVersion = '0.9.31'
object Panel1: TPanel
Left = 0
Height = 50
Top = 585
Width = 742
Align = alBottom
BevelOuter = bvNone
ClientHeight = 50
ClientWidth = 742
TabOrder = 1
object CloseBtn: TBitBtn
AnchorSideRight.Control = Panel1
AnchorSideRight.Side = asrBottom
Left = 657
Height = 30
Top = 10
Width = 75
Anchors = [akRight]
BorderSpacing.Right = 10
DefaultCaption = True
Kind = bkClose
ModalResult = 11
TabOrder = 2
end
object SaveBtn: TBitBtn
AnchorSideRight.Control = CloseBtn
Left = 572
Height = 30
Top = 10
Width = 75
Anchors = [akRight]
BorderSpacing.Around = 10
Caption = '&Save to file'
TabOrder = 1
end
object CopyBtn: TBitBtn
AnchorSideRight.Control = SaveBtn
Left = 487
Height = 30
Top = 10
Width = 75
Anchors = [akRight]
BorderSpacing.Around = 10
Caption = 'CopyBtn'
OnClick = CopyBtnClick
TabOrder = 0
end
end
inline LogMemo: TSynEdit
Left = 98
Height = 150
Top = 120
Width = 200
Font.Height = -13
Font.Name = 'Courier New'
Font.Pitch = fpFixed
Font.Quality = fqNonAntialiased
ParentColor = False
ParentFont = False
TabOrder = 0
Gutter.Width = 21
Gutter.MouseActions = <
item
ClickCount = ccAny
ClickDir = cdDown
Command = emcOnMainGutterClick
end
item
Button = mbRight
Command = emcContextMenu
end>
RightGutter.Width = 0
RightGutter.MouseActions = <
item
ClickCount = ccAny
ClickDir = cdDown
Command = emcOnMainGutterClick
end
item
Button = mbRight
Command = emcContextMenu
end>
Keystrokes = <
item
Command = ecUp
ShortCut = 38
end
item
Command = ecSelUp
ShortCut = 8230
end
item
Command = ecScrollUp
ShortCut = 16422
end
item
Command = ecDown
ShortCut = 40
end
item
Command = ecSelDown
ShortCut = 8232
end
item
Command = ecScrollDown
ShortCut = 16424
end
item
Command = ecLeft
ShortCut = 37
end
item
Command = ecSelLeft
ShortCut = 8229
end
item
Command = ecWordLeft
ShortCut = 16421
end
item
Command = ecSelWordLeft
ShortCut = 24613
end
item
Command = ecRight
ShortCut = 39
end
item
Command = ecSelRight
ShortCut = 8231
end
item
Command = ecWordRight
ShortCut = 16423
end
item
Command = ecSelWordRight
ShortCut = 24615
end
item
Command = ecPageDown
ShortCut = 34
end
item
Command = ecSelPageDown
ShortCut = 8226
end
item
Command = ecPageBottom
ShortCut = 16418
end
item
Command = ecSelPageBottom
ShortCut = 24610
end
item
Command = ecPageUp
ShortCut = 33
end
item
Command = ecSelPageUp
ShortCut = 8225
end
item
Command = ecPageTop
ShortCut = 16417
end
item
Command = ecSelPageTop
ShortCut = 24609
end
item
Command = ecLineStart
ShortCut = 36
end
item
Command = ecSelLineStart
ShortCut = 8228
end
item
Command = ecEditorTop
ShortCut = 16420
end
item
Command = ecSelEditorTop
ShortCut = 24612
end
item
Command = ecLineEnd
ShortCut = 35
end
item
Command = ecSelLineEnd
ShortCut = 8227
end
item
Command = ecEditorBottom
ShortCut = 16419
end
item
Command = ecSelEditorBottom
ShortCut = 24611
end
item
Command = ecToggleMode
ShortCut = 45
end
item
Command = ecCopy
ShortCut = 16429
end
item
Command = ecPaste
ShortCut = 8237
end
item
Command = ecDeleteChar
ShortCut = 46
end
item
Command = ecCut
ShortCut = 8238
end
item
Command = ecDeleteLastChar
ShortCut = 8
end
item
Command = ecDeleteLastChar
ShortCut = 8200
end
item
Command = ecDeleteLastWord
ShortCut = 16392
end
item
Command = ecUndo
ShortCut = 32776
end
item
Command = ecRedo
ShortCut = 40968
end
item
Command = ecLineBreak
ShortCut = 13
end
item
Command = ecSelectAll
ShortCut = 16449
end
item
Command = ecCopy
ShortCut = 16451
end
item
Command = ecBlockIndent
ShortCut = 24649
end
item
Command = ecLineBreak
ShortCut = 16461
end
item
Command = ecInsertLine
ShortCut = 16462
end
item
Command = ecDeleteWord
ShortCut = 16468
end
item
Command = ecBlockUnindent
ShortCut = 24661
end
item
Command = ecPaste
ShortCut = 16470
end
item
Command = ecCut
ShortCut = 16472
end
item
Command = ecDeleteLine
ShortCut = 16473
end
item
Command = ecDeleteEOL
ShortCut = 24665
end
item
Command = ecUndo
ShortCut = 16474
end
item
Command = ecRedo
ShortCut = 24666
end
item
Command = ecGotoMarker0
ShortCut = 16432
end
item
Command = ecGotoMarker1
ShortCut = 16433
end
item
Command = ecGotoMarker2
ShortCut = 16434
end
item
Command = ecGotoMarker3
ShortCut = 16435
end
item
Command = ecGotoMarker4
ShortCut = 16436
end
item
Command = ecGotoMarker5
ShortCut = 16437
end
item
Command = ecGotoMarker6
ShortCut = 16438
end
item
Command = ecGotoMarker7
ShortCut = 16439
end
item
Command = ecGotoMarker8
ShortCut = 16440
end
item
Command = ecGotoMarker9
ShortCut = 16441
end
item
Command = ecSetMarker0
ShortCut = 24624
end
item
Command = ecSetMarker1
ShortCut = 24625
end
item
Command = ecSetMarker2
ShortCut = 24626
end
item
Command = ecSetMarker3
ShortCut = 24627
end
item
Command = ecSetMarker4
ShortCut = 24628
end
item
Command = ecSetMarker5
ShortCut = 24629
end
item
Command = ecSetMarker6
ShortCut = 24630
end
item
Command = ecSetMarker7
ShortCut = 24631
end
item
Command = ecSetMarker8
ShortCut = 24632
end
item
Command = ecSetMarker9
ShortCut = 24633
end
item
Command = EcFoldLevel1
ShortCut = 41009
end
item
Command = EcFoldLevel2
ShortCut = 41010
end
item
Command = EcFoldLevel1
ShortCut = 41011
end
item
Command = EcFoldLevel1
ShortCut = 41012
end
item
Command = EcFoldLevel1
ShortCut = 41013
end
item
Command = EcFoldLevel6
ShortCut = 41014
end
item
Command = EcFoldLevel7
ShortCut = 41015
end
item
Command = EcFoldLevel8
ShortCut = 41016
end
item
Command = EcFoldLevel9
ShortCut = 41017
end
item
Command = EcFoldLevel0
ShortCut = 41008
end
item
Command = EcFoldCurrent
ShortCut = 41005
end
item
Command = EcUnFoldCurrent
ShortCut = 41003
end
item
Command = EcToggleMarkupWord
ShortCut = 32845
end
item
Command = ecNormalSelect
ShortCut = 24654
end
item
Command = ecColumnSelect
ShortCut = 24643
end
item
Command = ecLineSelect
ShortCut = 24652
end
item
Command = ecTab
ShortCut = 9
end
item
Command = ecShiftTab
ShortCut = 8201
end
item
Command = ecMatchBracket
ShortCut = 24642
end
item
Command = ecColSelUp
ShortCut = 40998
end
item
Command = ecColSelDown
ShortCut = 41000
end
item
Command = ecColSelLeft
ShortCut = 40997
end
item
Command = ecColSelRight
ShortCut = 40999
end
item
Command = ecColSelPageDown
ShortCut = 40994
end
item
Command = ecColSelPageBottom
ShortCut = 57378
end
item
Command = ecColSelPageUp
ShortCut = 40993
end
item
Command = ecColSelPageTop
ShortCut = 57377
end
item
Command = ecColSelLineStart
ShortCut = 40996
end
item
Command = ecColSelLineEnd
ShortCut = 40995
end
item
Command = ecColSelEditorTop
ShortCut = 57380
end
item
Command = ecColSelEditorBottom
ShortCut = 57379
end>
MouseActions = <
item
ShiftMask = [ssShift, ssAlt]
ClickDir = cdDown
Command = emcStartSelections
MoveCaret = True
end
item
Shift = [ssShift]
ShiftMask = [ssShift, ssAlt]
ClickDir = cdDown
Command = emcStartSelections
MoveCaret = True
Option = 1
end
item
Shift = [ssAlt]
ShiftMask = [ssShift, ssAlt]
ClickDir = cdDown
Command = emcStartColumnSelections
MoveCaret = True
end
item
Shift = [ssShift, ssAlt]
ShiftMask = [ssShift, ssAlt]
ClickDir = cdDown
Command = emcStartColumnSelections
MoveCaret = True
Option = 1
end
item
Button = mbRight
Command = emcContextMenu
end
item
ClickCount = ccDouble
ClickDir = cdDown
Command = emcSelectWord
MoveCaret = True
end
item
ClickCount = ccTriple
ClickDir = cdDown
Command = emcSelectLine
MoveCaret = True
end
item
ClickCount = ccQuad
ClickDir = cdDown
Command = emcSelectPara
MoveCaret = True
end
item
Button = mbMiddle
ClickDir = cdDown
Command = emcPasteSelection
MoveCaret = True
end
item
Shift = [ssCtrl]
ShiftMask = [ssShift, ssAlt, ssCtrl]
Command = emcMouseLink
end>
MouseSelActions = <
item
ClickDir = cdDown
Command = emcStartDragMove
end>
Lines.Strings = (
'LogMemo'
)
VisibleSpecialChars = [vscSpace, vscTabAtLast]
ReadOnly = True
ScrollBars = ssAutoBoth
BracketHighlightStyle = sbhsBoth
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 17
MouseActions = <>
MarkupInfo.Background = clBtnFace
MarkupInfo.Foreground = clNone
DigitCount = 2
ShowOnlyLineNumbersMultiplesOf = 1
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 4
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
end
end
object SaveDialog: TSaveDialog
Filter = 'Text files|*.txt|All files|*'
Options = [ofOverwritePrompt, ofEnableSizing, ofViewDetail]
left = 40
top = 320
end
end

View File

@ -0,0 +1,102 @@
unit ResultDlg;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
ExtCtrls, Buttons, ClipBrd, LCLType, LCLProc, SynEdit, SynHighlighterPo;
type
{ TResultDlgForm }
TResultDlgForm = class(TForm)
CopyBtn: TBitBtn;
SaveBtn: TBitBtn;
CloseBtn: TBitBtn;
Panel1: TPanel;
SaveDialog: TSaveDialog;
FLog: TStringList;
LogMemo: TSynEdit;
procedure CopyBtnClick(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormShow(Sender: TObject);
private
{ private declarations }
PoHL: TSynPoSyn;
procedure SaveToFile;
public
{ public declarations }
property Log: TStringList read FLog write FLog;
end;
implementation
{$R *.lfm}
const
sSaveError = 'Error saving file:'^m'%s';
{ TResultDlgForm }
procedure TResultDlgForm.FormCreate(Sender: TObject);
begin
LogMemo.Lines.Clear;
LogMemo.Align := alClient;
FLog := TStringList.Create;
PoHL := TSynPoSyn.Create(Self);
LogMemo.Highlighter := PoHL;
end;
procedure TResultDlgForm.FormClose(Sender: TObject;
var CloseAction: TCloseAction);
begin
FLog.Clear;
end;
procedure TResultDlgForm.CopyBtnClick(Sender: TObject);
begin
ClipBoard.AsText := LogMemo.Text;
end;
procedure TResultDlgForm.FormDestroy(Sender: TObject);
begin
FLog.Free;
end;
procedure TResultDlgForm.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_Tab) and (Shift = []) and LogMemo.Focused then
begin
//Workaroud: cannot tab out of LogMemo
CopyBtn.SetFocus;
//debugln('Tab');
Key := 0;
end;
end;
procedure TResultDlgForm.FormShow(Sender: TObject);
begin
LogMemo.Lines.Assign(FLog);
end;
procedure TResultDlgForm.SaveToFile;
begin
if SaveDialog.Execute then
begin
try
LogMemo.Lines.SaveToFile(SaveDialog.FileName);
except
MessageDlg('GPoCheck',Format(sSaveError,[SaveDialog.FileName]), mtError, [mbOk], 0);
end;
end;
end;
end.

File diff suppressed because it is too large Load Diff