unit unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, types, chmsitemap, chmfilewriter,
  Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, Menus, ExtCtrls, EditBtn,
  LazFileUtils, UTF8Process;

type

  { TCHMForm }

  TCHMForm = class(TForm)
    AddFilesBtn: TButton;
    AutoAddLinksBtn: TButton;
    AddAllBtn: TButton;
    CompileViewBtn: TButton;
    CompileBtn: TButton;
    DefaultPageCombo: TComboBox;
    ChmFileNameEdit: TFileNameEdit;
    FollowLinksCheck: TCheckBox;
    CreateSearchableCHMCheck: TCheckBox;
    CompileTimeOptionsLabel: TLabel;
    FilesNoteLabel: TLabel;
    DefaultPageLabel: TLabel;
    CHMFilenameLabel: TLabel;
    OpenDialog2: TOpenDialog;
    RemoveFilesBtn: TButton;
    TOCEditBtn: TButton;
    IndexEditBtn: TButton;
    IndexEdit: TFileNameEdit;
    GroupBox1: TGroupBox;
    FileListBox: TListBox;
    TableOfContentsLabel: TLabel;
    IndexLabel: TLabel;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    ProjSaveItem: TMenuItem;
    ProjSaveAsItem: TMenuItem;
    MenuItem12: TMenuItem;
    ProjQuitItem: TMenuItem;
    CompileItem: TMenuItem;
    CompileProjItem: TMenuItem;
    CompileOpenBttn: TMenuItem;
    ProjCloseItem: TMenuItem;
    MenuItem3: TMenuItem;
    HelpHelpItem: TMenuItem;
    MenuItem5: TMenuItem;
    HelpAboutItem: TMenuItem;
    ProjNewItem: TMenuItem;
    ProjOpenItem: TMenuItem;
    MenuItem9: TMenuItem;
    OpenDialog1: TOpenDialog;
    MainPanel: TPanel;
    Panel2: TPanel;
    SaveDialog1: TSaveDialog;
    StatusBar1: TStatusBar;
    TOCEdit: TFileNameEdit;
    procedure AddAllBtnClick(Sender: TObject);
    procedure AddFilesBtnClick(Sender: TObject);
    procedure AutoAddLinksBtnClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ChmFileNameEditAcceptFileName(Sender: TObject; var Value: String);
    procedure CompileBtnClick(Sender: TObject);
    procedure CompileViewBtnClick(Sender: TObject);
    procedure FileListBoxDrawItem({%H-}Control: TWinControl; Index: Integer;
      ARect: TRect; {%H-}State: TOwnerDrawState);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure IndexEditAcceptFileName(Sender: TObject; var Value: String);
    procedure IndexEditBtnClick(Sender: TObject);
    procedure ProjCloseItemClick(Sender: TObject);
    procedure ProjNewItemClick(Sender: TObject);
    procedure ProjOpenItemClick(Sender: TObject);
    procedure ProjQuitItemClick(Sender: TObject);
    procedure ProjSaveAsItemClick(Sender: TObject);
    procedure ProjSaveItemClick(Sender: TObject);
    procedure RemoveFilesBtnClick(Sender: TObject);
    procedure TOCEditAcceptFileName(Sender: TObject; var Value: String);
    procedure TOCEditBtnClick(Sender: TObject);
  private
    FModified: Boolean;
    procedure AddItems({%H-}AParentItem: TTreeNode; {%H-}ChmItems: TChmSiteMapItems);

    function GetModified: Boolean;
    procedure Save(aAs: Boolean);
    procedure CloseProject;

    procedure AddFilesToProject(Strings: TStrings);
    procedure InitFileDialog(Dlg: TFileDialog);
    procedure ProjectDirChanged;
    function CreateRelativeProjectFile(Filename: string): string;
    function CreateAbsoluteProjectFile(Filename: string): string;
  public
    Project: TChmProject;
    procedure OpenProject(AFileName: String);
    // Dirty flag: has project been modified since opening?
    property Modified: Boolean read GetModified write FModified;
  end; 

var
  CHMForm: TCHMForm;

implementation

{$R *.lfm}

uses CHMSiteMapEditor, LHelpControl, Process;

{ TCHMForm }

procedure TCHMForm.AddItems(AParentItem: TTreeNode; ChmItems: TChmSiteMapItems);
  begin
{    for I := 0 to ChmItems.Count-1 do begin
      Item := TreeView1.Items.AddChild(AParentItem, ChmItems.Item[I].Text);
      AddItems(Item, ChmItems.Item[I].Children);
    end;
 } end;

procedure TCHMForm.Button1Click(Sender: TObject);
begin
  {SiteMap := TChmSiteMap.Create(stTOC);
  OpenDialog1.InitialDir := GetCurrentDir;
  if OpenDialog1.Execute = False then Exit;
  SiteMap.LoadFromFile(OpenDialog1.FileName);
  AddItems(nil, sitemap.Items);
  
  Stream := TMemoryStream.Create;
  
  Sitemap.SaveToStream(Stream);
  Stream.Position := 0;
  
  SynEdit1.Lines.LoadFromStream(Stream);
  Stream.Free;
   }
end;

procedure TCHMForm.AddFilesBtnClick(Sender: TObject);
begin
  InitFileDialog(OpenDialog2);
  if OpenDialog2.Execute = False then exit;
  Modified := True;
  AddFilesToProject(OpenDialog2.Files);
end;

procedure TCHMForm.AddAllBtnClick(Sender: TObject);
var
  Files: TStrings;
  procedure AddDir(ADir: String);
  var
    SearchRec: TSearchRec;
    FileName: String;
  begin
    // WriteLn('Adding Dir: ', ADir);
    if FindFirst(ADir+'*', faAnyFile or faDirectory, SearchRec) = 0 then
    begin
      repeat
        if (SearchRec.Attr and faDirectory) <> 0 then
        begin
          if Pos('.', SearchRec.Name) = 0 then
          begin
            AddDir(IncludeTrailingPathDelimiter(ADir+SearchRec.Name));
          end;
        end
        else
        begin
          FileName := ADir+SearchRec.Name;
          FileName := ExtractRelativepath(Project.ProjectDir, FileName);
          if Files.IndexOf(FileName) = -1 then
            Files.Add(FileName);
        end;
      until FindNext(SearchRec) <> 0;
      FindClose(SearchRec);
    end;
  end;
begin
  if MessageDlg('This will add all files in the project directory ' + LineEnding +
                'recursively. Do you want to continue?',
                mtConfirmation, [mbYes, mbNo],0) = mrNo then exit;
  Modified := True;
  Files := TStringList.Create;
  try
    Files.AddStrings(FileListBox.Items);
    AddDir(Project.ProjectDir);
    FileListBox.Items.Assign(Files);
  finally
    Files.Free;
  end;
end;

procedure TCHMForm.AutoAddLinksBtnClick(Sender: TObject);
begin
  Modified := True;
end;

procedure TCHMForm.Button2Click(Sender: TObject);
begin
    {
  if OpenDialog1.Execute = False then Exit;
  OutStream := TFileStream.Create('/home/andrew/test.chm', fmCreate or fmOpenWrite);
  Chm := TChmWriter.Create(OutStream, False);
  Chm.FilesToCompress.AddStrings(OpenDialog1.Files);
  Chm.GetFileData := @GetData;
  Chm.Title := 'test';
  Chm.DefaultPage := 'index.html';
  Chm.Execute;
  OutStream.Free;
  Chm.Free;
     }
  
  
end;

procedure TCHMForm.ChmFileNameEditAcceptFileName(Sender: TObject; var Value: String);
begin
  if ExtractFileExt(Value) = '' then Value := Value+'.chm';
end;

procedure TCHMForm.CompileBtnClick(Sender: TObject);
var
  OutFile: TFileStream;
begin
  if ChmFileNameEdit.FileName = '' then
  begin
    MessageDlg('You must set a filename for the output CHM file!', mtError, [mbCancel], 0);
    Exit;
  end;
  Save(False);
  OutFile := TFileStream.Create(Project.OutputFileName, fmCreate or fmOpenWrite);
  try
    Project.WriteChm(OutFile);
    ShowMessage('CHM file '+ChmFileNameEdit.FileName+' was created.');
  finally
    OutFile.Free;
  end;
end;


procedure TCHMForm.CompileViewBtnClick(Sender: TObject);
var
  LHelpName: String;
  LHelpConn: TLHelpConnection;
  Proc: TProcessUTF8;
  ext: String;
begin
  if ChmFileNameEdit.FileName = '' then
  begin
    MessageDlg('You must set a filename for the output CHM file!', mtError, [mbCancel], 0);
    Exit;
  end;
  CompileBtnClick(Sender);
  // open
  // ...
  ext := ExtractFileExt(Application.ExeName);
  LHelpName := '../../components/chmhelp/lhelp/lhelp' + ext;
  if not FileExists(LHelpName) then
  begin
    if MessageDlg('LHelp could not be located at '+ LHelpName +' Try to build using lazbuild?', mtError, [mbCancel, mbYes], 0) = mrYes then
    begin
      if not FileExists('../../lazbuild' + ext) then
      begin
        MessageDlg('lazbuild coul not be found.', mtError, [mbCancel], 0);
        Exit;
      end;
      Proc := TProcessUTF8.Create(Self);
      Proc.CommandLine := '../../../lazbuild ./lhelp.lpi';
      SetCurrentDir('../../components/chmhelp/lhelp/');
      Proc.Options := [poWaitOnExit];
      Proc.Execute;
      SetCurrentDir('../../../tools/chmmaker/');
      if Proc.ExitStatus <> 0 then
      begin
        MessageDlg('lhelp failed to build', mtError, [mbCancel], 0);
        Exit;
      end;
      Proc.Free;
    end
    else
      Exit;
  end;
  LHelpConn := TLHelpConnection.Create;
  try
    LHelpConn.StartHelpServer('chmmaker', LHelpName);
    LHelpConn.OpenFile(ChmFileNameEdit.FileName);
  finally
    LHelpConn.Free;
  end;
end;

procedure TCHMForm.FileListBoxDrawItem(Control: TWinControl; Index: Integer;
  ARect: TRect; State: TOwnerDrawState);
begin
  FileListbox.Canvas.FillRect(ARect);
  if Pos('..', FileListBox.Items.Strings[Index]) > 0 then
  begin
    // These items won't be added to the chm because they are not within the project dir
    // so mark them with a red rectangle
    Dec(ARect.Right);
    Dec(ARect.Bottom);
    FileListBox.Canvas.Pen.Color := clRed;
    FileListBox.Canvas.Frame(ARect);
  end;
  // Draw item text
  FileListBox.Canvas.TextRect(ARect,
    2, (ARect.Top + ARect.Bottom - FileListbox.Canvas.TextHeight('Tg')) div 2,
    FileListBox.Items[Index]
  );
end;

procedure TCHMForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
  MResult: Integer;
begin
  if Modified then
  begin
    MResult := MessageDlg('Project is modified would you like to save the changes?', mtConfirmation,
                                      [mbYes, mbNo, mbCancel], 0);
    case MResult of
      mrYes: Save(False);
      mrNo: CloseAction := caFree;
      mrCancel: CloseAction := caNone;
    end;
   end;
end;

procedure TCHMForm.FormCreate(Sender: TObject);
begin
  CloseProject;
end;

procedure TCHMForm.FormDestroy(Sender: TObject);
begin
  CloseProject;
end;

procedure TCHMForm.IndexEditAcceptFileName(Sender: TObject; var Value: String);
begin
  Modified := True;
  //Value := ExtractRelativepath(Project.ProjectDir, Value);
  //WriteLn(Value);
  Project.IndexFileName := Value;
end;

procedure TCHMForm.IndexEditBtnClick(Sender: TObject);
var
  Stream: TStream;
  FileName: String;
begin
  FileName := IndexEdit.FileName;
  if FileName = '' then
  begin
    FileName := Project.ProjectDir+'_index.hhk'
  end;

  if FileExists(FileName) then
  begin
    Stream := TFileStream.Create(FileName, fmOpenReadWrite);
  end
  else
  begin
    Stream := TFileStream.Create(FileName, fmCreate or fmOpenReadWrite);
  end;

  try
    if SitemapEditForm.Execute(Stream, stIndex, FileListBox.Items) then IndexEdit.FileName := FileName;
  finally
    Stream.Free;
  end;
end;

procedure TCHMForm.ProjCloseItemClick(Sender: TObject);
begin
  CloseProject;
end;

procedure TCHMForm.ProjNewItemClick(Sender: TObject);
begin
  InitFileDialog(SaveDialog1);
  If SaveDialog1.Execute then
  begin
    if FileExists(SaveDialog1.FileName)
    and (MessageDlg('File Already Exists! Ovewrite?', mtWarning, [mbYes, mbNo],0) = mrNo) then Exit;
    OpenProject(SaveDialog1.FileName);
    Project.SaveToFile(SaveDialog1.FileName);
  end;
end;

procedure TCHMForm.ProjOpenItemClick(Sender: TObject);
begin
  InitFileDialog(OpenDialog1);
  if OpenDialog1.Execute then
  begin
    CloseProject;
    OpenProject(OpenDialog1.FileName);
  end;
end;

procedure TCHMForm.ProjQuitItemClick(Sender: TObject);
begin
  Close;
end;

procedure TCHMForm.ProjSaveAsItemClick(Sender: TObject);
begin
  Save(True);
end;

procedure TCHMForm.ProjSaveItemClick(Sender: TObject);
begin
  Save(False);
end;

procedure TCHMForm.RemoveFilesBtnClick(Sender: TObject);
var
  I: Integer;
begin
  Modified := True;
  for I := FileListBox.Items.Count-1 downto 0 do
    if FileListBox.Selected[I] then FileListBox.Items.Delete(I);
  DefaultPageCombo.Items.Assign(FileListBox.Items);
end;

procedure TCHMForm.TOCEditAcceptFileName(Sender: TObject; var Value: String);
begin
  Modified := True;
  Project.TableOfContentsFileName := Value;
end;

procedure TCHMForm.TOCEditBtnClick(Sender: TObject);
var
  Stream: TStream;
  FileName: String;
  BDir: String;
begin
  FileName := TOCEdit.FileName;
  if FileName = '' then
  begin
    FileName := Project.ProjectDir+'_table_of_contents.hhc'
  end;
  
  if FileExists(FileName) then
  begin
    Stream := TFileStream.Create(FileName, fmOpenReadWrite);
  end
  else
  begin
    Stream := TFileStream.Create(FileName, fmCreate or fmOpenReadWrite);
  end;

  try
    BDir := ExtractFilePath(Project.FileName);
    FileName := ExtractRelativepath(BDir, FileName);
    if SitemapEditForm.Execute(Stream, stTOC, FileListBox.Items) then TOCEdit.FileName := FileName;
  finally
    Stream.Free;
  end;
end;

function TCHMForm.GetModified: Boolean;
begin
  Result := (Project <> nil) and FModified;
end;

procedure TCHMForm.Save(aAs: Boolean);
begin
  if aAs or (Project.FileName = '') then
  begin
    InitFileDialog(SaveDialog1);
    if SaveDialog1.Execute then
    begin
      Project.FileName := ChangeFileExt(SaveDialog1.FileName,'.hfp');
      ProjectDirChanged;
    end;
  end;
  Project.Files.Assign(FileListBox.Items);
  Project.TableOfContentsFileName := CreateRelativeProjectFile(TOCEdit.FileName);
  Project.IndexFileName           := CreateRelativeProjectFile(IndexEdit.FileName);
  Project.DefaultPage             := DefaultPageCombo.Text;
  Project.AutoFollowLinks         := FollowLinksCheck.Checked;
  Project.MakeSearchable          := CreateSearchableCHMCheck.Checked;
  Project.OutputFileName          := CreateRelativeProjectFile(ChmFileNameEdit.FileName);

  Project.SaveToFile(Project.FileName);
  Modified := False;
end;

procedure TCHMForm.CloseProject;
begin
  FileListBox.Clear;
  DefaultPageCombo.Clear;
  TOCEdit.Clear;
  IndexEdit.Clear;
  GroupBox1.Enabled      := False;
  MainPanel.Enabled         := False;
  CompileItem.Enabled    := False;
  ProjSaveAsItem.Enabled := False;
  ProjSaveItem.Enabled   := False;
  ProjCloseItem.Enabled  := False;

  FollowLinksCheck.Checked := False;
  CreateSearchableCHMCheck.Checked := False;
  FreeAndNil(Project);
end;

procedure TCHMForm.OpenProject(AFileName: String);
begin
  if not Assigned(Project) then Project := TChmProject.Create;
  Project.LoadFromFile(AFileName);
  GroupBox1.Enabled      := True;
  MainPanel.Enabled      := True;
  CompileItem.Enabled    := True;
  ProjSaveAsItem.Enabled := True;
  ProjSaveItem.Enabled   := True;
  ProjCloseItem.Enabled  := True;

  FileListBox.Items.AddStrings(Project.Files);
  TOCEdit.FileName := Project.TableOfContentsFileName;
  IndexEdit.FileName := Project.IndexFileName;
  DefaultPageCombo.Items.Assign(FileListBox.Items);
  DefaultPageCombo.Text := Project.DefaultPage;
  FollowLinksCheck.Checked := Project.AutoFollowLinks;
  CreateSearchableCHMCheck.Checked := Project.MakeSearchable;
  ChmFileNameEdit.FileName := Project.OutputFileName;

  ProjectDirChanged;
end;

procedure TCHMForm.AddFilesToProject(Strings: TStrings);
var
  BDir: String;
  I: Integer;
  RelativePath: String;
  FileName: String;
begin
  Modified := True;
  BDir := ExtractFilePath(Project.FileName);

  for I := 0 to Strings.Count-1 do begin
    FileName := Strings.Strings[I];

    RelativePath := ExtractRelativepath(BDir, FileName);
    if Pos('..', RelativePath) > 0 then
      FileListBox.Items.AddObject(RelativePath, TObject(1))
    else
      FileListBox.Items.AddObject(RelativePath, TObject(0));
  end;
  DefaultPageCombo.Items.Assign(FileListBox.Items);
end;

procedure TCHMForm.InitFileDialog(Dlg: TFileDialog);
var
  Dir: String;
begin
  Dir:='';
  if (Project<>nil) then
    Dir:=ExtractFilePath(Project.FileName);
  if not DirPathExists(Dir) then
    Dir:=GetCurrentDirUTF8;
  Dlg.InitialDir:=Dir;
end;

procedure TCHMForm.ProjectDirChanged;
var
  Dir: String;
begin
  if Project=nil then exit;
  Dir:=ExtractFilePath(Project.FileName);

  TOCEdit.InitialDir:=Dir;
  IndexEdit.InitialDir:=Dir;
  ChmFileNameEdit.InitialDir:=Dir;
end;

function TCHMForm.CreateRelativeProjectFile(Filename: string): string;
begin
  Result:=Filename;
  if (Project=nil) or (not FilenameIsAbsolute(Project.FileName)) then exit;
  Result:=CreateRelativePath(Filename,ExtractFilePath(Project.FileName));
end;

function TCHMForm.CreateAbsoluteProjectFile(Filename: string): string;
begin
  Result:=Filename;
  if FilenameIsAbsolute(Result) then exit;
  if (Project=nil) or (not FilenameIsAbsolute(Project.FileName)) then exit;
  Result:=ExtractFilePath(Project.FileName)+Filename;
end;

end.

