lazarus/components/pochecker/graphstat.pp
2020-03-31 14:19:29 +00:00

482 lines
13 KiB
ObjectPascal

unit GraphStat;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Types, FileUtil, Forms, Controls, Graphics, Dialogs,
{$ifndef POCHECKERSTANDALONE}
LazIDEIntf,
{$else}
Process, Utf8Process,
{$endif}
ExtCtrls, PoFamilies, PoFamilyLists, PoCheckerConsts, LCLProc, StdCtrls, ComCtrls, Menus,
PoCheckerSettings, LCLIntf, Translations;
type
{ TGraphStatForm }
TGraphStatForm = class(TForm)
Separator1MenuItem: TMenuItem;
RefreshCurrMenuItem: TMenuItem;
RefreshAllMenuItem: TMenuItem;
Separator2MenuItem: TMenuItem;
POEditorMenuItem: TMenuItem;
ExtEditorMenuItem: TMenuItem;
IDEEditorMenuItem: TMenuItem;
ContextPopupMenu: TPopupMenu;
StatusLabel: TLabel;
ListView: TListView;
TranslatedLabel: TLabel;
UnTranslatedLabel: TLabel;
FuzzyLabel: TLabel;
LegendPanel: TPanel;
TranslatedShape: TShape;
UnTranslatedShape: TShape;
FuzzyShape: TShape;
procedure ExtEditorMenuItemClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure IDEEditorMenuItemClick(Sender: TObject);
procedure ListViewMouseMove(Sender: TObject; {%H-}Shift: TShiftState; X,
Y: Integer);
procedure ListViewMouseUp(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
procedure POEditorMenuItemClick(Sender: TObject);
procedure RefreshAllMenuItemClick(Sender: TObject);
procedure RefreshCurrMenuItemClick(Sender: TObject);
private
{ private declarations }
FImgList: TImageList;
FOldHintHidePause: Integer;
FSettings: TPoCheckerSettings;
Fn: string;
CurrentMasterPoFile: string;
procedure LoadConfig;
Procedure SaveConfig;
function CreateBitmap(AStat: TStat): TBitmap;
procedure AddToListView(AStat: TStat; ABmp: TBitmap);
procedure DrawGraphs(Cnt: PtrInt);
procedure MaybeOpenInLazIDE;
procedure MaybeOpenInExternalEditor;
procedure RefreshTranslations(All: boolean);
procedure ConfigureContextPopUp(AdvancedMode: boolean);
public
{ public declarations }
property Settings: TPoCheckerSettings read FSettings write FSettings;
end;
var
GraphStatForm: TGraphStatForm;
implementation
{$R *.lfm}
{ TGraphStatForm }
const
Radius = 40; //when we have anti-aliasing we can reduce this
BmpWH = 2 * Radius;
clBackGround = clWhite;
clTranslated = clGreen;
clUnTranslated = clRed;
clFuzzy = clFuchsia;
{
ListView
@designtime
AutoSort := False
ReadOnly := True
ScrollBars := ssAutoBoth
ViewStyle := vsIcon
@runtime
Color := clBackGround
LargeImages := FImgList;
Align := alClient;
}
procedure TGraphStatForm.FormShow(Sender: TObject);
begin
FOldHintHidePause := Application.HintHidePause;
Application.HintHidePause := 5000;
LoadConfig;
WindowState := Settings.GraphFormWindowState;
Application.QueueAsyncCall(@DrawGraphs, PoFamilyList.PoFamilyStats.Count);
end;
procedure TGraphStatForm.IDEEditorMenuItemClick(Sender: TObject);
begin
MaybeOpenInLazIDE;
end;
procedure TGraphStatForm.ListViewMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
Item: TListItem;
Index: Integer;
AStat: TStat;
begin
Item := Listview.GetItemAt(X, Y);
if Assigned(Item) then
begin
Index := Item.Index;
AStat := PoFamilyList.PoFamilyStats.Items[Index];
ListView.Hint := Format(sStatHint,[AStat.NrTranslated, AStat.PercTranslated,
AStat.NrUnTranslated, AStat.PercUnTranslated,
AStat.NrFuzzy, AStat.PercFuzzy, AStat.NrErrors]);
end
else
begin
ListView.Hint := '';
Application.HideHint;
end;
end;
procedure TGraphStatForm.ListViewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
anItem: TListItem;
anIndex: Integer;
AStat: TStat;
CurrScreenPoint: TPoint;
begin
if Button = mbRight then
begin
anItem := Listview.GetItemAt(X, Y);
if Assigned(anItem) then
begin
anIndex := anItem.Index;
AStat := PoFamilyList.PoFamilyStats.Items[anIndex];
Fn := AStat.PoName;
CurrentMasterPoFile := ExtractMasterNameFromChildName(AStat.PoName);
ConfigureContextPopUp(ssShift in Shift);
CurrScreenPoint := ListView.ClientToScreen(Point(X, Y));
ContextPopupMenu.PopUp(CurrScreenPoint.X, CurrScreenPoint.Y);
end;
end;
end;
procedure TGraphStatForm.POEditorMenuItemClick(Sender: TObject);
begin
if OpenDocument(Fn) = false then
ShowMessage(Format(SOpenFail,[Fn]));
end;
procedure TGraphStatForm.RefreshAllMenuItemClick(Sender:
TObject);
begin
RefreshTranslations(true);
end;
procedure TGraphStatForm.RefreshCurrMenuItemClick(Sender: TObject);
begin
RefreshTranslations(false);
end;
procedure TGraphStatForm.RefreshTranslations(All: boolean);
var
i: integer;
AbortUpdate: boolean;
begin
//"All" parameter defines if we refresh current family only (false) or all families (true)
StatusLabel.Visible := true;
RefreshCurrMenuItem.Enabled := false;
RefreshAllMenuItem.Enabled := false;
try
AbortUpdate := false;
i := 0;
while (i<PoFamilyList.Count) and (AbortUpdate=false) do
begin
try
if All=true then
StatusLabel.Caption := Format(sProcessingTranslationFamilyOf, [
IntToStr(i + 1), IntToStr(PoFamilyList.Count)])
else
StatusLabel.Caption := sProcessingTranslationFamily;
StatusLabel.Repaint;
if All=true then
begin
UpdatePoFileTranslations(PoFamilyList.Items[i].MasterName);
inc(i);
end
else
begin
UpdatePoFileTranslations(CurrentMasterPoFile);
AbortUpdate := true;
end;
except
on E: EPOFileError do
if MessageDlg('POChecker', Format(
sCannotWriteFileYouCanPressRetryToRefreshThisTransl, [LineEnding,
E.ResFileName, LineEnding, LineEnding]), mtError, [mbRetry, mbAbort
], 0) = mrAbort then
AbortUpdate := true;
end;
end;
finally
StatusLabel.Visible := false;
RefreshCurrMenuItem.Enabled := true;
RefreshAllMenuItem.Enabled := true;
end;
end;
procedure TGraphStatForm.ConfigureContextPopUp(AdvancedMode: boolean);
begin
//POEditorMenuItem is always shown, others only in advanced mode
Separator1MenuItem.Visible := AdvancedMode;
//ExtEditorMenuItem and IDEEditorMenuItem are invisible by default
{$ifdef pocheckerstandalone}
ExtEditorMenuItem.Visible := AdvancedMode;
if Settings.ExternalEditorName <> '' then
ExtEditorMenuItem.Enabled := true;
{$else}
IDEEditorMenuItem.Visible := AdvancedMode;
{$endif}
Separator2MenuItem.Visible := AdvancedMode;
RefreshCurrMenuItem.Visible := AdvancedMode;
RefreshAllMenuItem.Visible := AdvancedMode;
end;
procedure TGraphStatForm.LoadConfig;
var
ARect: TRect;
begin
if not Assigned(FSettings) then Exit;
ARect := FSettings.GraphFormGeometry;
//debugln('TGraphStatForm.LoadConfig: ARect = ',dbgs(ARect));
if not IsDefaultRect(ARect) and IsValidRect(ARect) then
begin
ARect := FitToRect(ARect, Screen.WorkAreaRect);
BoundsRect := ARect;
end;
end;
procedure TGraphStatForm.SaveConfig;
begin
//debugln('TGraphStatForm.SaveConfig: BoundsRect = ',dbgs(BoundsRect));
if not Assigned(FSettings) then Exit;
Settings.GraphFormWindowState := WindowState;
if (WindowState = wsNormal) then
Settings.GraphFormGeometry := BoundsRect
else
Settings.GraphFormGeometry := Rect(RestoredLeft, RestoredTop, RestoredLeft + RestoredWidth, RestoredTop + RestoredHeight);
end;
procedure TGraphStatForm.FormCreate(Sender: TObject);
begin
Caption := sGrapStatFormCaption;
POEditorMenuItem.Caption := sOpenFileInSystemPOEditor;
ExtEditorMenuItem.Caption := sOpenFileInExternalEditor;
IDEEditorMenuItem.Caption := sOpenFileInIDEEditor;
RefreshCurrMenuItem.Caption := sRefreshCurrentTranslationFamily;
RefreshAllMenuItem.Caption := sRefreshAllTranslationFamilies;
TranslatedShape.Brush.Color := clTranslated;
UnTranslatedShape.Brush.Color := clUntranslated;
FuzzyShape.Brush.Color := clFuzzy;
ListView.Color := clBackGround;
ListView.Align := alClient;
end;
procedure TGraphStatForm.FormClose(Sender: TObject;
var CloseAction: TCloseAction);
begin
Application.HintHidePause := FOldHintHidePause;
end;
procedure TGraphStatForm.FormActivate(Sender: TObject);
begin
//Doing this in TGraphStatForm.FormShow results in icons disappearing in Linux GTK2
//DrawGraphs;
end;
procedure TGraphStatForm.ExtEditorMenuItemClick(Sender: TObject);
begin
MaybeOpenInExternalEditor;
end;
procedure TGraphStatForm.FormDestroy(Sender: TObject);
begin
if Assigned(FImgList) then FImgList.Free;
SaveConfig;
end;
function TGraphStatForm.CreateBitmap(AStat: TStat): TBitmap;
const
FullCircle = 16 * 360;
QMark = ' ? ';
var
Bmp: TBitmap;
Translated16Angle, UnTranslated16Angle, Fuzzy16Angle: Integer;
PieRect: TRect;
TextSize: TSize;
begin
Bmp := TBitmap.Create;
Bmp.SetSize(BmpWH,BmpWH);
Bmp.Canvas.AntialiasingMode := amOn; // currently effective only with Qt
PieRect := Rect(0,0,BmpWH, BmpWH);
with Bmp do
begin
//Draw background
Canvas.Brush.Color := clBackGround;
Canvas.FillRect(PieRect);
Canvas.Pen.Color := clBackGround;
//All angles in 16th of a degree
Translated16Angle := Round(AStat.FracTranslated * FullCircle);
UnTranslated16Angle := Round(AStat.FracUntranslated * FullCircle);
Fuzzy16Angle := Round(AStat.FracFuzzy * FullCircle);
if Translated16Angle > 0 then
begin
Canvas.Brush.Color:= clTranslated;
if Translated16Angle = FullCircle then
Canvas.Ellipse(PieRect)
else
Canvas.RadialPie(PieRect.Left,PieRect.Top,PieRect.Right,PieRect.Bottom,0,Translated16Angle);;
end;
if UnTranslated16Angle > 0 then
begin
Canvas.Brush.Color:= clUnTranslated;
if UnTranslated16Angle = FullCircle then
Canvas.Ellipse(PieRect)
else
Canvas.RadialPie(PieRect.Left,PieRect.Top,PieRect.Right,PieRect.Bottom,Translated16Angle,UnTranslated16Angle);;
end;
if Fuzzy16Angle > 0 then
begin
Canvas.Brush.Color:= clFuzzy;
if Fuzzy16Angle = FullCircle then
Canvas.Ellipse(PieRect)
else
Canvas.RadialPie(PieRect.Left,PieRect.Top,PieRect.Right,PieRect.Bottom,Translated16Angle+UnTranslated16Angle,Fuzzy16Angle);
end;
if AStat.NrErrors <> 0 then begin
Bmp.Canvas.Font := Font;
Canvas.Font.Size := BmpWH div 6;
Canvas.Font.Style:= [fsBold];
Canvas.Font.Color:= clRed;
TextSize := Bmp.Canvas.TextExtent(QMark);
Canvas.Brush.Color:= clBlack;
Canvas.Rectangle(0,PieRect.Bottom-TextSize.cy-4,TextSize.cx+4,PieRect.Bottom);
Canvas.Brush.Color:= clYellow;
Canvas.TextOut(2,PieRect.Bottom-TextSize.cy-2,QMark);
end;
end;
Result := Bmp;
end;
procedure TGraphStatForm.AddToListView(AStat: TStat; ABmp: TBitmap);
var
ImgIndex: Integer;
ListItem: TListItem;
begin
ImgIndex := FImgList.AddMasked(ABmp, clBackGround);
ListItem := ListView.Items.Add;
ListItem.Caption := AStat.ShortPoName;
ListItem.ImageIndex := ImgIndex;
end;
procedure TGraphStatForm.DrawGraphs(Cnt: PtrInt);
var
Bmp: TBitmap;
AStat: TStat;
Index: Integer;
begin
if Assigned(FImgList) then FImgList.Free;
FImgList := TImageList.CreateSize(BmpWH, BmpWH);
ListView.Clear;
ListView.LargeImages := FImgList;
ListView.BeginUpdate;
Screen.BeginWaitCursor;
try
StatusLabel.Visible := True;
for Index := 0 to PoFamilyList.PoFamilyStats.Count - 1 do
begin
StatusLabel.Caption := Format(sCreatingIconXofY,[Index + 1, Cnt]);
StatusLabel.Repaint;
AStat := PoFamilyList.PoFamilyStats.Items[Index];
Bmp := CreateBitmap(AStat);
AddToListView(AStat, Bmp);
//if there are many icons to draw, occasionally update the ListView as visual feedback
if (((Index + 1) mod 25) = 0) then
begin
ListView.EndUpdate;
ListView.Repaint;
ListView.BeginUpdate;
end;
Bmp.Free;
end;
finally
ListView.EndUpdate;
Screen.EndWaitCursor;
StatusLabel.Visible := False;
end;
end;
procedure TGraphStatForm.MaybeOpenInExternalEditor;
{$ifdef POCHECKERSTANDALONE}
var
Proc: TProcessUtf8;
{$endif}
begin
{$ifdef POCHECKERSTANDALONE}
Proc := TProcessUtf8.Create(nil);
try
Proc.Options := [];
Proc.Executable := Settings.ExternalEditorName;
Proc.Parameters.Add(Fn);
try
Proc.Execute;
except
on E: EProcess do
begin
debugln('TGraphStatForm.ListViewMouseUp:');
debugln(' Exception occurred of type ',E.ClassName);
debugln(' Message: ',E.Message);
ShowMessage(Format(SOpenFailExternal,[Fn,Settings.ExternalEditorName]));
end;
end;
finally
Proc.Free;
end;
{$endif}
end;
procedure TGraphStatForm.MaybeOpenInLazIDE;
{$ifndef POCHECKERSTANDALONE}
var
mr: TModalResult;
PageIndex,WindowIndex: Integer;
OpenFlags: TOpenFlags;
{$endif}
begin
{$ifndef POCHECKERSTANDALONE}
PageIndex:= -1;
WindowIndex:= -1;
OpenFlags:= [ofOnlyIfExists,ofAddToRecent,ofRegularFile,ofConvertMacros];
mr := LazarusIde.DoOpenEditorFile(Fn,PageIndex,WindowIndex,OpenFlags);
if mr = mrOk then
ModalResult:= mrOpenEditorFile //To let caller know what we want to do
else
ShowMessage(Format(SOpenFail,[Fn]));
{$endif}
end;
end.