unit HtmFileExp1;

{$mode objfpc}{$H+}

{.$define UsePreview}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons,
  {$ifdef UsePreview}
  OsPrinters,
  {$endif}
  IpUtils, IpHtml, IpHtmlNodes,
  ExtCtrls, StdCtrls, FileUtil;

type
  TSimpleIpHtml = class(TIpHtml)
  public
    property OnGetImageX;
  end;

  TPst = class(TObject)
    Position: Integer;
  end;

  { TFHtmFileExp1 }

  TFHtmFileExp1 = class(TForm)
    B_OpenHTMLFile: TButton;
    IpHtmlPanel1: TIpHtmlPanel;
    OpenDialog1: TOpenDialog;
    P_Top: TPanel;
    SB_GoBackward: TSpeedButton;
    SB_GoForward: TSpeedButton;
    procedure B_OpenHTMLFileClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure HTMLGetImageX(Sender: TIpHtmlNode; const URL: string;
      var Picture: TPicture);
    procedure IpHtmlPanel1HotClick(Sender: TObject);
    procedure SB_GoBackwardClick(Sender: TObject);
    procedure SB_GoForwardClick(Sender: TObject);
  private
    SL: TStringList;
    CurrPos: Integer;
    CurrFile: string;
    Path: string;
    PathChanged: Boolean;
    procedure GoBackFor (GoBack: Boolean);
    procedure OpenHTMLFile(const Filename: string;
      ToAdd, RelativePath: Boolean);
  end;

var
  FHtmFileExp1: TFHtmFileExp1;

function ProgramDirectory(BundleRoot: boolean): string;

implementation

uses
  LazFileUtils, LazUTF8;

{$R htmfileexp1.lfm}
{$R defaultimage.res}

function ProgramDirectory(BundleRoot: boolean): string;
const
  BundlePostFix='.app/Contents/MacOS';
begin
  Result:=FileUtil.ProgramDirectory;
  if BundleRoot
  and (RightStr(ChompPathDelim(Result),length(BundlePostFix))=BundlePostFix) then
    Result:=ExtractFilePath(LeftStr(Result,length(Result)-length(BundlePostFix)));
end;


{--------------------------------------}
{-EVENTS-----------}

procedure TFHtmFileExp1.FormCreate(Sender: TObject);
var
  Dir: String;
  Filename: String;
begin
  SL := TStringList.Create;
  CurrPos := -1;
  Dir:=ProgramDirectory(false);
  SetCurrentDirUTF8(Dir);
  Filename:=ParamStrUTF8(1);
  if not FileExistsUTF8(Filename) then
    Filename:='index.html';
  OpenHTMLFile (Filename, True, False);
end;

procedure TFHtmFileExp1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for  I := SL.Count-1 downto 0  do
    TPst(SL.Objects[I]).Free;
  SL.Free;
end;

procedure TFHtmFileExp1.B_OpenHTMLFileClick(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    OpenHtmlFile (OpenDialog1.FileName, True, False);
  end;
end;

procedure TFHtmFileExp1.HTMLGetImageX(Sender: TIpHtmlNode; const URL: string;
  var Picture: TPicture);
var
  PicCreated: Boolean;
  FN, nURL: string;
begin
  PicCreated := False;
  try
    if  PathChanged
    then  FN := Path
    else  FN := ExtractFilePath(SL[CurrPos]);
    if  Pos ('\',FN) <> 0
    then  nURL := NetToDOSPath(URL)
    else  nURL := URL;
    FN := Concat (FN, nURL);
    if FileExistsUTF8(FN) then begin
      if Picture = nil then begin
          Picture := TPicture.Create;
          PicCreated := True;
        end;
        Picture.LoadFromFile(FN);
    end;
  except
    if PicCreated then
      Picture.Free;
    Picture := nil;
  end;
end {HTMLGetImageX};

procedure TFHtmFileExp1.IpHtmlPanel1HotClick(Sender: TObject);
begin
  if  IpHtmlPanel1.HotNode is TIpHtmlNodeA  then  begin
    TPst(SL.Objects[CurrPos]).Position := IpHtmlPanel1.VScrollPos;
    OpenHTMLFile (TIpHtmlNodeA(IpHtmlPanel1.HotNode).HRef, True, True);
  end;
end;

procedure TFHtmFileExp1.SB_GoBackwardClick(Sender: TObject);
begin
  TPst(SL.Objects[CurrPos]).Position := IpHtmlPanel1.VScrollPos;
  GoBackFor (True);
end;

procedure TFHtmFileExp1.SB_GoForwardClick(Sender: TObject);
begin
  GoBackFor (False);
end;

{-PRIVATE----------}

procedure TFHtmFileExp1.GoBackFor (GoBack: Boolean);
var
  Pst: TPst;
  S: string;
  SameFile: Boolean;
begin
  if GoBack
  then  Dec (CurrPos)
  else  Inc (CurrPos);
  if GoBack then  begin
    SameFile := SL[CurrPos+1] = SL[CurrPos]
  end
  else  begin
    if  CurrPos > 0
    then  SameFile := SL[CurrPos-1] = SL[CurrPos]
    else  SameFile := False;
  end;
  if  SameFile
  then  S := ''
  else  S := SL[CurrPos];
  Pst := TPst(SL.Objects[CurrPos]);
  OpenHTMLFile (S, False, True);
  IpHtmlPanel1.VScrollPos := Pst.Position;
  SB_GoBackward.Enabled := (SL.Count > 1) and (CurrPos > 0);
  SB_GoForward.Enabled := (SL.Count > 1) and (CurrPos < SL.Count-1);
end {GoBackFor};

procedure TFHtmFileExp1.OpenHTMLFile(const Filename: string;
  ToAdd, RelativePath: Boolean);

procedure UpdateSB(const FN: string);
var
  Pst: TPst;
  I: Integer;
begin
  if ToAdd then  begin
    Pst := TPst.Create;
    Pst.Position := IpHtmlPanel1.VScrollPos;
    for  I := SL.Count-1 downto CurrPos+1  do  begin
      TPst(SL.Objects[I]).Free;
      SL.Delete(I);
    end;
    CurrPos := SL.AddObject (FN, Pst);
    SB_GoBackward.Enabled := SL.Count > 1;
    SB_GoForward.Enabled := False;
  end;
end {UpdateSB};

var
  fs: TFileStream;
  NewHTML: TSimpleIpHtml;
  P: Integer;
  FN, Anchor: string;
begin
  if  Filename = ''  then  begin
    if  CurrPos > -1
    then  IpHtmlPanel1.VScrollPos := 0;
    Exit;
  end;
  P := Pos ('#', Filename);
  FN := Filename;
  if RelativePath then  begin
    PathChanged := False;
    if  P = 0  then  begin
      Anchor := '';
    end
    else if  P = 1  then  begin
      FN := Concat (Path, CurrFile);
      Anchor := Copy (Filename, 2, MaxInt);
      IpHtmlPanel1.MakeAnchorVisible (Anchor);
      UpdateSB(FN);
      Exit;
    end
    else  begin
      FN := Copy (Filename, 1, P-1);
      Anchor := Copy (Filename, P+1, MaxInt);
    end;
    if ToAdd then  begin
      FN := Concat (Path, FN);
    end;
  end
  else begin
    FN := ExpandFileNameUTF8(FN);
    CurrFile := ExtractFileName (FN);
    Path := ExtractFilePath (FN);
    PathChanged := True;
  end;
  try
    fs := TFileStream.Create (FN, fmOpenRead);
    try
      NewHTML := TSimpleIpHtml.Create; // Beware: Will be freed automatically by IpHtmlPanel1
      NewHTML.OnGetImageX := @HTMLGetImageX;
      NewHTML.LoadFromStream (fs);
      IpHtmlPanel1.SetHtml (NewHTML);
      if  Anchor <> ''
      then  IpHtmlPanel1.MakeAnchorVisible (Anchor);
      UpdateSB(FN);
    finally
      fs.Free;
    end;
  except
    on E: Exception do begin
      MessageDlg ('Unable to open HTML file'+sLineBreak+
        'HTML File: '+Filename+sLineBreak+
        'Error: '+E.Message, mtError, [mbCancel], 0);
    end;
  end;
end {OpenHTMLFile};

end.

