{ *************************************************************************** * * * 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 . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** } unit AboutFrm; {$mode objfpc}{$H+} interface uses Classes, SysUtils, // LCL Forms, Controls, Graphics, StdCtrls, Buttons, ExtCtrls, ComCtrls, Menus, LCLIntf, LazConf, InterfaceBase, LCLPlatformDef, Clipbrd, LCLVersion, // LazUtils FPCAdds, LazFileUtils, // IDE LazarusIDEStrConsts, EnvironmentOpts; type { TScrollingText } TScrollingText = class(TGraphicControl) private FActive: boolean; FActiveLine: integer; //the line over which the mouse hovers FBuffer: TBitmap; FEndLine: integer; FLineHeight: integer; FLines: TStrings; FNumLines: integer; FOffset: integer; FStartLine: integer; FStepSize: integer; FTimer: TTimer; function ActiveLineIsURL: boolean; procedure DoTimer(Sender: TObject); procedure SetActive(const AValue: boolean); procedure Init; procedure DrawScrollingText(Sender: TObject); protected procedure DoOnChangeBounds; override; procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override; procedure MouseMove(Shift: TShiftState; X,Y: Integer); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Active: boolean read FActive write SetActive; property Lines: TStrings read FLines write FLines; end; { TAboutForm } TAboutForm = class(TForm) CloseButton: TBitBtn; BuildDateLabel: TLABEL; AboutMemo: TMEMO; CopyToClipboardButton: TSpeedButton; DocumentationLabel: TLabel; DocumentationURLLabel: TLabel; FPCVersionLabel: TLabel; LogoImage: TImage; miVerToClipboard: TMenuItem; OfficialLabel: TLabel; OfficialURLLabel: TLabel; VersionPage: TTabSheet; ButtonPanel: TPanel; PlatformLabel: TLabel; PopupMenu1: TPopupMenu; VersionLabel: TLABEL; RevisionLabel: TLabel; Notebook: TPageControl; AboutPage: TTabSheet; ContributorsPage: TTabSheet; AcknowledgementsPage:TTabSheet; procedure AboutFormCreate(Sender:TObject); procedure CopyToClipboardButtonClick(Sender: TObject); procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction); procedure FormShow(Sender: TObject); procedure miVerToClipboardClick(Sender: TObject); procedure NotebookPageChanged(Sender: TObject); procedure URLLabelMouseDown(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer); procedure URLLabelMouseEnter(Sender: TObject); procedure URLLabelMouseLeave(Sender: TObject); private Acknowledgements: TScrollingText; Contributors: TScrollingText; procedure LoadContributors; procedure LoadAcknowledgements; procedure LoadLogo; public end; function ShowAboutForm: TModalResult; implementation {$R *.lfm} uses GraphUtil, IDEImagesIntf; function ShowAboutForm: TModalResult; var AboutForm: TAboutForm; begin AboutForm:=TAboutForm.Create(nil); Result:=AboutForm.ShowModal; AboutForm.Free; end; // Compiler generated date string is in form y/m/d and time string in hh:mm:ss. // This function gives a custom string respresentation. function GetCustomBuildDate: string; var Parts: TStringArray; begin Parts := (LazarusBuildDateStr+'-'+LazarusBuildTimeStr).Split(['/','-',':']); Result := Parts[0]+'-'+Parts[1]+'-'+Parts[2]+' '+Parts[3]+':'+Parts[4]; end; { TAboutForm } procedure TAboutForm.AboutFormCreate(Sender:TObject); const DoubleLineEnding = LineEnding + LineEnding; begin Notebook.PageIndex:=0; Caption:=lisAboutLazarus; VersionLabel.Caption := lisVersion+': '+LazarusVersionStr; RevisionLabel.Caption := lisRevision+LazarusRevisionStr; BuildDateLabel.Caption := lisBuildDate+': '+GetCustomBuildDate; FPCVersionLabel.Caption:= lisFPCVersion+{$I %FPCVERSION%}; PlatformLabel.Caption:=GetCompiledTargetCPU+'-'+GetCompiledTargetOS +'-'+LCLPlatformDisplayNames[GetDefaultLCLWidgetType]; VersionPage.Caption:=lisVersion; AboutPage.Caption:=lisMenuTemplateAbout; ContributorsPage.Caption:=lisContributors; ContributorsPage.DoubleBuffered := True; AcknowledgementsPage.Caption:=lisAcknowledgements; AcknowledgementsPage.DoubleBuffered := True; miVerToClipboard.Caption := lisVerToClipboard; VersionLabel.Font.Color:= clWhite; AboutMemo.Lines.Text:= Format(lisAboutLazarusMsg,[DoubleLineEnding,DoubleLineEnding,DoubleLineEnding]); OfficialLabel.Caption := lisAboutOfficial; OfficialURLLabel.Caption := 'https://www.lazarus-ide.org'; DocumentationLabel.Caption := lisAboutDocumentation; DocumentationURLLabel.Caption := 'https://wiki.freepascal.org'; LoadContributors; LoadAcknowledgements; CloseButton.Caption := lisBtnClose; CopyToClipboardButton.Caption := ''; CopyToClipboardButton.Images := IDEImages.Images_16; CopyToClipboardButton.ImageIndex := IDEImages.LoadImage('laz_copy'); CopyToClipboardButton.Hint := lisVerToClipboard; end; procedure TAboutForm.CopyToClipboardButtonClick(Sender: TObject); begin miVerToClipboardClick(nil); end; procedure TAboutForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin Acknowledgements.Active := False; Contributors.Active := False; end; procedure TAboutForm.FormShow(Sender: TObject); begin LoadLogo; end; procedure TAboutForm.miVerToClipboardClick(Sender: TObject); begin Clipboard.AsText := 'Lazarus ' + LazarusVersionStr + ' (rev ' + LazarusRevisionStr + ')' + ' FPC ' + {$I %FPCVERSION%} + ' ' + PlatformLabel.Caption; end; procedure TAboutForm.NotebookPageChanged(Sender: TObject); begin if Assigned(Contributors) then Contributors.Active:=NoteBook.ActivePage = ContributorsPage; if Assigned(Acknowledgements) then Acknowledgements.Active:=NoteBook.ActivePage = AcknowledgementsPage; CopyToClipboardButton.Visible := Notebook.ActivePage = VersionPage; end; procedure TAboutForm.URLLabelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin OpenURL(TLabel(Sender).Caption); end; procedure TAboutForm.URLLabelMouseLeave(Sender: TObject); begin TLabel(Sender).Font.Style := []; TLabel(Sender).Font.Color := clBlue; TLabel(Sender).Cursor := crDefault; end; procedure TAboutForm. URLLabelMouseEnter(Sender: TObject); begin TLabel(Sender).Font.Style := [fsUnderLine]; TLabel(Sender).Font.Color := clRed; TLabel(Sender).Cursor := crHandPoint; end; procedure TAboutForm.LoadContributors; var ContributorsFileName: string; begin ContributorsPage.ControlStyle := ContributorsPage.ControlStyle - [csOpaque]; Contributors := TScrollingText.Create(ContributorsPage); Contributors.Name:='Contributors'; Contributors.Parent := ContributorsPage; Contributors.Align:=alClient; ContributorsFileName:= AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory) +'docs'+PathDelim+'Contributors.txt'; //debugln('TAboutForm.LoadContributors ',FileExistsUTF8(ContributorsFileName),' ',ContributorsFileName); if FileExistsUTF8(ContributorsFileName) then Contributors.Lines.LoadFromFile(ContributorsFileName) else Contributors.Lines.Text:=lisAboutNoContributors; end; procedure TAboutForm.LoadAcknowledgements; var AcknowledgementsFileName: string; begin Acknowledgements := TScrollingText.Create(AcknowledgementsPage); Acknowledgements.Name:='Acknowledgements'; Acknowledgements.Parent := AcknowledgementsPage; Acknowledgements.Align:=alClient; AcknowledgementsFileName:= AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory) +'docs'+PathDelim+'acknowledgements.txt'; if FileExistsUTF8(AcknowledgementsFileName) then Acknowledgements.Lines.LoadFromFile(AcknowledgementsFileName) else Acknowledgements.Lines.Text:=lisAboutNoContributors; end; procedure TAboutForm.LoadLogo; var W, H: Integer; ScaleFactor: Double; begin LogoImage.Picture.LoadFromResourceName(HInstance, 'splash_logo', TPortableNetworkGraphic); ScaleFactor := GetCanvasScaleFactor; // Usually 1.0, but on macOS = 2.0 W := round(LogoImage.Width * ScaleFactor); H := round(LogoImage.Height * ScaleFactor); ScaleImg(LogoImage.Picture.Bitmap, W, H); end; { TScrollingText } procedure TScrollingText.SetActive(const AValue: boolean); begin FActive := AValue; if FActive then Init; FTimer.Enabled:=Active; end; procedure TScrollingText.Init; begin FBuffer.Width := Width; FBuffer.Height := Height; FLineHeight := FBuffer.Canvas.TextHeight('X'); FNumLines := FBuffer.Height div FLineHeight; if FOffset = -1 then FOffset := FBuffer.Height; with FBuffer.Canvas do begin Brush.Color := clWhite; Brush.Style := bsSolid; FillRect(0, 0, Width, Height); end; end; procedure TScrollingText.DrawScrollingText(Sender: TObject); begin if Active then Canvas.Draw(0,0,FBuffer); end; procedure TScrollingText.DoTimer(Sender: TObject); var w: integer; s: string; i: integer; begin if not Active then Exit; Dec(FOffset, FStepSize); if FOffSet < 0 then FStartLine := -FOffset div FLineHeight else FStartLine := 0; FEndLine := FStartLine + FNumLines + 1; if FEndLine > FLines.Count - 1 then FEndLine := FLines.Count - 1; FBuffer.Canvas.FillRect(Rect(0, 0, FBuffer.Width, FBuffer.Height)); for i := FEndLine downto FStartLine do begin s := Trim(FLines[i]); //reset buffer font FBuffer.Canvas.Font.Style := []; FBuffer.Canvas.Font.Color := clBlack; //skip empty lines if Length(s) > 0 then begin //check for bold format token if s[1] = '#' then begin s := copy(s, 2, Length(s) - 1); FBuffer.Canvas.Font.Style := [fsBold]; end else begin //check for url if (Pos('http://', s) = 1) or (Pos('https://', s) = 1) then begin if i = FActiveLine then begin FBuffer.Canvas.Font.Style := [fsUnderline]; FBuffer.Canvas.Font.Color := clRed; end else FBuffer.Canvas.Font.Color := clBlue; end; end; w := FBuffer.Canvas.TextWidth(s); FBuffer.Canvas.TextOut((FBuffer.Width - w) div 2, FOffset + i * FLineHeight, s); end; end; //start showing the list from the start if FStartLine > FLines.Count - 1 then FOffset := FBuffer.Height; Invalidate; end; function TScrollingText.ActiveLineIsURL: boolean; begin if (FActiveLine > 0) and (FActiveLine < FLines.Count) then Result := (Pos('http://', FLines[FActiveLine]) = 1) or (Pos('https://', FLines[FActiveLine]) = 1) else Result := False; end; procedure TScrollingText.DoOnChangeBounds; begin inherited DoOnChangeBounds; Init; end; procedure TScrollingText.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseDown(Button, Shift, X, Y); if ActiveLineIsURL then OpenURL(FLines[FActiveLine]); end; procedure TScrollingText.MouseMove(Shift: TShiftState; X, Y: Integer); begin inherited MouseMove(Shift, X, Y); //calculate what line is clicked from the mouse position FActiveLine := (Y - FOffset) div FLineHeight; Cursor := crDefault; if (FActiveLine >= 0) and (FActiveLine < FLines.Count) and ActiveLineIsURL then Cursor := crHandPoint; end; constructor TScrollingText.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := ControlStyle + [csOpaque]; OnPaint := @DrawScrollingText; FLines := TStringList.Create; FTimer := TTimer.Create(nil); FTimer.OnTimer:=@DoTimer; FTimer.Interval:=30; FBuffer := TBitmap.Create; FStepSize := 1; FStartLine := 0; FOffset := -1; end; destructor TScrollingText.Destroy; begin FLines.Free; FTimer.Free; FBuffer.Free; inherited Destroy; end; function GetLazarusRevision: string; begin result := LazarusRevisionStr; end; initialization lcl_revision_func := @GetLazarusRevision; end.