{ Copyright (C) 2006 Mattias Gaertner

  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., 51 Franklin Street - Fifth Floor,
  Boston, MA 02110-1335, USA.
}
unit H2PasDlg;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,
  // LCL
  LCLProc, LCLType, LResources, Forms, Controls, Graphics,
  Dialogs, ComCtrls, Buttons, StdCtrls, ExtCtrls,
  // LazUtils
  LazConfigStorage, LazFileUtils, LazFileCache, LazUTF8,
  // SynEdit
  SynEdit,
  // CodeTools
  FileProcs,
  // IdeIntf
  BaseIDEIntf, IDEMsgIntf, MenuIntf, IDECommands, IDEDialogs, LazIDEIntf,
  SrcEditorIntf, IDEExternToolIntf, CompOptsIntf, IDETextConverter,
  // H2Pas
  H2PasStrConsts, H2PasConvert, IDETextConvListEdit;

type

  { TH2PasDialog }

  TH2PasDialog = class(TForm)
    MergeAllCHeadersExceptCurrentButton: TButton;
    MergeFileCheckBox: TCheckBox;
    FileInfoMemo: TMemo;
    FileStateImageList: TImageList;
    MoveFileDownButton: TButton;
    MoveFileUpButton: TButton;
    ConvertAndBuildButton: TButton;
    FileInfoGroupBox: TGroupBox;
    MainPageControl: TPageControl;
    AddIncludedCHeaderFilesButton: TButton;

    // c header files
    FilesTabSheet: TTabSheet;
    CHeaderFilesSplitter1: TSplitter;
    AddCHeadersButton: TButton;
    DisableAllCHeadersButton: TButton;
    EnableAllCHeadersButton: TButton;
    DeleteCHeadersButton: TButton;
    CHeaderFilesCheckTreeView: TTreeView;

    // pre h2pas
    PreH2PasTabSheet: TTabSheet;
    PreH2PasGroupBox: TGroupBox;
    PreH2PasEdit: TTextConvListEditor;

    // h2pas
    h2pasOptionsTabSheet: TTabSheet;
    h2pasOptionsCheckGroup: TCheckGroup;
    LibnameEdit: TEdit;
    LibNameLabel: TLabel;
    OutputExtEdit: TEdit;
    OutputExtLabel: TLabel;
    OutputDirEdit: TEdit;
    OutputDirLabel: TLabel;
    OutputDirBrowseButton: TButton;

    // post h2pas
    PostH2PasTabSheet: TTabSheet;
    PostH2PasGroupBox: TGroupBox;
    PostH2PasEdit: TTextConvListEditor;

    // settings
    SettingsTabSheet: TTabSheet;
    h2pasFilenameBrowseButton: TButton;
    H2PasFilenameEdit: TEdit;
    H2PasFilenameLabel: TLabel;
    NewSettingsButton: TButton;
    SaveSettingsAsButton: TButton;
    OpenLastProjectOnStartCheckBox: TCheckBox;

    // buttons at bottom
    OpenSettingsButton: TButton;
    SaveSettingsButton: TButton;
    ConvertButton: TButton;
    CloseButton: TButton;

    procedure AddCHeadersButtonClick(Sender: TObject);
    procedure AddIncludedCHeaderFilesButtonClick(Sender: TObject);
    procedure CHeaderFilesCheckTreeViewDblClick(Sender: TObject);
    procedure CHeaderFilesCheckTreeViewMouseDown(Sender: TOBject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure CHeaderFilesCheckTreeViewSelectionChanged(Sender: TObject);
    procedure CloseButtonClick(Sender: TObject);
    procedure ConvertAndBuildButtonClick(Sender: TObject);
    procedure ConvertButtonClick(Sender: TObject);
    procedure DeleteCHeadersButtonClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure H2PasFilenameEditEditingDone(Sender: TObject);
    procedure LibnameEditEditingDone(Sender: TObject);
    procedure MergeAllCHeadersExceptCurrentButtonClick(Sender: TObject);
    procedure MergeFileCheckBoxEditingDone(Sender: TObject);
    procedure MoveFileDownButtonClick(Sender: TObject);
    procedure MoveFileUpButtonClick(Sender: TObject);
    procedure NewSettingsButtonClick(Sender: TObject);
    procedure OpenLastProjectOnStartCheckBoxChange(Sender: TObject);
    procedure OpenSettingsButtonClick(Sender: TObject);
    procedure OutputDirBrowseButtonClick(Sender: TObject);
    procedure OutputDirEditEditingDone(Sender: TObject);
    procedure OutputExtEditEditingDone(Sender: TObject);
    procedure PostH2PasEditModified(Sender: TObject);
    procedure PreH2PasEditModified(Sender: TObject);
    procedure SaveSettingsAsButtonClick(Sender: TObject);
    procedure SaveSettingsButtonClick(Sender: TObject);
    procedure EnableAllCHeadersButtonClick(Sender: TObject);
    procedure DisableAllCHeadersButtonClick(Sender: TObject);
    procedure h2pasFilenameBrowseButtonClick(Sender: TObject);
    procedure h2pasOptionsCheckGroupItemClick(Sender: TObject; Index: LongInt);
    procedure OnShowSrcEditSection(Sender: TObject);
    procedure OnAddSearchAndReplaceBeforeH2PasClick(Sender: TObject);
  private
    FConverter: TH2PasConverter;
    FSrcEditAddSearchReplaceMenuItem: TIDEMenuCommand;
    FSrcEditSection: TIDEMenuSection;
    
    function GetProject: TH2PasProject;
    
    procedure UpdateAll(ScanIncludes: boolean);
    procedure UpdateProjectChanged(ScanIncludes: boolean); // show project settings
    procedure UpdateCaption;
    procedure UpdateFileInfo;
    procedure ClearMessages;
    procedure CreateLazarusMenuItems;
    function GetNodeFilename(Node: TTreeNode): string;
    function GetCurrentCHeaderFile: TH2PasFile;
    procedure MoveCurrentFile(Offset: integer);
    function GetFileNodeStateIndex(aFile: TH2PasFile): Integer;
    procedure MarkAllCHeadersExceptCurrentToMerge;

    // project settings
    procedure UpdateFilesPage(ScanIncludes: boolean);
    procedure UpdateH2PasPage;
    procedure UpdateConvertPage;
    // global settings
    procedure UpdateSettingsPage;
    
    function ShowSelectDirDialog(const Title: string;
                                 var ADirectory: string): boolean;
    function ShowOpenFileDialog(const Title: string;
                                var AFilename: string): boolean;
    // IDE events
    function OnIDESavedAll(Sender: TObject): TModalResult;
  public
    function Convert: TModalResult;
    procedure ShowH2PasError;

    function SaveSettings: TModalResult;
    function SaveGlobalSettings: TModalResult;
    function LoadGlobalSettings: TModalResult;
    function SaveProject(const Filename: string; Flags: TSaveFlags): TModalResult;
    function OpenProject(const Filename: string; Flags: TOpenFlags): TModalResult;
    property Converter: TH2PasConverter read FConverter;
    property Project: TH2PasProject read GetProject;
    property SrcEditSection: TIDEMenuSection read FSrcEditSection;
    property SrcEditAddSearchReplaceMenuItem: TIDEMenuCommand
                                          read FSrcEditAddSearchReplaceMenuItem;
  end;

var
  H2PasDialog: TH2PasDialog = nil;
  CmdH2PasTool: TIDECommand = nil;

procedure ExecuteH2PasTool(Sender: TObject);


procedure Register;

implementation

{$R h2pasdlg.lfm}

procedure ExecuteH2PasTool(Sender: TObject);
begin
  if H2PasDialog=nil then
    H2PasDialog:=TH2PasDialog.Create(Application);
  H2PasDialog.ShowOnTop;
end;

procedure Register;
var
  Key: TIDEShortCut;
  Cat: TIDECommandCategory;
begin
  // register IDE shortcut and menu item
  Key := IDEShortCut(VK_UNKNOWN,[],VK_UNKNOWN,[]);
  Cat:=IDECommandList.FindCategoryByName(CommandCategoryToolMenuName);
  CmdH2PasTool := RegisterIDECommand(Cat,
         'H2Pas ...', h2pCreateUnitsFromCHeaderFiles, Key, nil, @ExecuteH2PasTool);
  RegisterIDEMenuCommand(itmSecondaryTools, 'H2PasTool', h2pH2Pas, nil, nil,
                         CmdH2PasTool);

  // register text converter tools
  TextConverterToolClasses.RegisterClass(TPreH2PasTools);
  TextConverterToolClasses.RegisterClass(TRemoveCPlusPlusExternCTool);
  TextConverterToolClasses.RegisterClass(TRemoveEmptyCMacrosTool);
  TextConverterToolClasses.RegisterClass(TReplaceEdgedBracketPairWithStar);
  TextConverterToolClasses.RegisterClass(TReplaceMacro0PointerWithNULL);
  TextConverterToolClasses.RegisterClass(TConvertFunctionTypesToPointers);
  TextConverterToolClasses.RegisterClass(TConvertEnumsToTypeDef);
  TextConverterToolClasses.RegisterClass(TCommentComplexCMacros);
  TextConverterToolClasses.RegisterClass(TCommentComplexCFunctions);
  TextConverterToolClasses.RegisterClass(TAddMissingMacroBrackets);

  TextConverterToolClasses.RegisterClass(TPostH2PasTools);
  TextConverterToolClasses.RegisterClass(TReplaceUnitFilenameWithUnitName);
  TextConverterToolClasses.RegisterClass(TRemoveIncludeDirectives);
  TextConverterToolClasses.RegisterClass(TRemoveDoubleSemicolons);
  TextConverterToolClasses.RegisterClass(TRemoveSystemTypes);
  TextConverterToolClasses.RegisterClass(TRemoveRedefinedPointerTypes);
  TextConverterToolClasses.RegisterClass(TRemoveEmptyTypeVarConstSections);
  TextConverterToolClasses.RegisterClass(TFixH2PasMissingIFDEFsInUnit);
  TextConverterToolClasses.RegisterClass(TReduceCompilerDirectivesInUnit);
  TextConverterToolClasses.RegisterClass(TReplaceImplicitTypes);
  TextConverterToolClasses.RegisterClass(TFixArrayOfParameterType);
  TextConverterToolClasses.RegisterClass(TAddMissingPointerTypes);
  TextConverterToolClasses.RegisterClass(TRemoveRedefinitionsInUnit);
  TextConverterToolClasses.RegisterClass(TFixAliasDefinitionsInUnit);
  TextConverterToolClasses.RegisterClass(TReplaceConstFunctionsInUnit);
  TextConverterToolClasses.RegisterClass(TReplaceTypeCastFunctionsInUnit);
  TextConverterToolClasses.RegisterClass(TFixForwardDefinitions);
  TextConverterToolClasses.RegisterClass(TAddToUsesSection);

  // register h2pas output parser
  ExternalToolList.RegisterParser(TH2PasParser);
end;

{ TH2PasDialog }

procedure TH2PasDialog.FormCreate(Sender: TObject);
begin
  Caption:=h2pCHeaderFileConverter;
  FilesTabSheet.Caption := h2pCHeaderFiles;
    AddCHeadersButton.Caption := h2pAddHFiles;
    DeleteCHeadersButton.Caption := h2pDeleteSelectedHFiles;
    EnableAllCHeadersButton.Caption := h2pEnableAllHFiles;
    DisableAllCHeadersButton.Caption := h2pDisableAllHFiles;
    MoveFileDownButton.Caption := h2pMoveFileDown;
    MoveFileUpButton.Caption := h2pMoveFileUp;
    FileInfoGroupBox.Caption := h2pFileInformation;
    AddIncludedCHeaderFilesButton.Caption := h2pAddIncludedHFiles;
    MergeAllCHeadersExceptCurrentButton.Caption := h2pMergeAllButThis;
  MergeFileCheckBox.Caption := h2pMergeFile;
  h2pasOptionsTabSheet.Caption := h2pH2pasOptions;
    h2pasOptionsCheckGroup.Caption := h2pOptions;
    with h2pasOptionsCheckGroup.Items do begin
      Add(h2pDUseExternalForAllProcedures);
      Add(h2pDUseExternalLibnameNameFunc__nameForFunctions);
      Add(h2pEConstantsInsteadOfEnumerationTypeForCEnums);
      Add(h2pCCompactOutputmodeLessSpacesAndEmptyLines);
      Add(h2pICreateAnIncludeFileInsteadOfAUnit);
      Add(h2pPUseLetterPForPointerTypesInsteadOf);
      Add(h2pPrPackAllRecords1ByteAlignment);
      Add(h2pPUseProcVarsForImports);
      Add(h2pSStripComments);
      Add(h2pSStripCommentsAndInfo);
      Add(h2pTPrependTypedefTypesWithT);
      Add(h2pTPrependTypedefTypesWithTAndRemove__);
      Add(h2pVReplacePointerParametersByVar);
      Add(h2pWHandleSpecialWin32Macros);
      Add(h2pXHandleSYS__TRAPOfThePalmOSHeaderFiles);
      Add(h2pCUseTypesInCtypesUnit);
    end;
    OutputExtLabel.Caption := h2pOutputExtensionOfNewFile;
    OutputDirLabel.Caption := h2pOutputDirectory;
    LibNameLabel.Caption := h2pLLibraryName;
  PreH2PasTabSheet.Caption := h2pBeforeH2pas;
    PreH2PasGroupBox.Caption := h2pConversionsBeforeRunningH2pas;
  PostH2PasTabSheet.Caption := h2pAfterH2pas;
    PostH2PasGroupBox.Caption := h2pConversionsAfterRunningH2pas;
  SettingsTabSheet.Caption := h2pSettings;
    H2PasFilenameLabel.Caption := h2pH2pasProgramPath;
    OpenLastProjectOnStartCheckBox.Caption := h2pOpenLastSettingsOnStart;
    SaveSettingsAsButton.Caption := h2pSaveSettingsAs;
    NewSettingsButton.Caption := h2pNewClearSettings;
  OpenSettingsButton.Caption := h2pOpenSettings;
  SaveSettingsButton.Caption := h2pSaveSettings;
  ConvertButton.Caption := h2pRunH2pas;
  ConvertAndBuildButton.Caption := h2pRunH2pasAndCompile;
  CloseButton.Caption := h2pClose;
  
  PreH2PasEdit:=TTextConvListEditor.Create(Self);
  with PreH2PasEdit do
  begin
    Name:='PreH2PasEdit';
    Align:=alClient;
    OnModified:=@PreH2PasEditModified;
    Dock(PreH2PasGroupBox, Rect(0, 0, 0, 0));
    Visible:=true;// Note: it's a form, and visible default is false
  end;

  PostH2PasEdit:=TTextConvListEditor.Create(Self);
  with PostH2PasEdit do begin
    Name:='PostH2PasEdit';
    Align:=alClient;
    OnModified:=@PostH2PasEditModified;
    Dock(PostH2PasGroupBox, Rect(0, 0, 0, 0));
    Visible:=true;// Note: it's a form, and visible default is false
  end;

  LazarusIDE.AddHandlerOnSavedAll(@OnIDESavedAll);
  CreateLazarusMenuItems;

  // create converter
  FConverter:=TH2PasConverter.Create;
  LoadGlobalSettings;
  
  // create project
  if Converter.AutoOpenLastProject
  and FileExistsUTF8(Converter.CurrentProjectFilename) then
    OpenProject(Converter.CurrentProjectFilename,[]);
  if Project=nil then begin
    Converter.Project:=TH2PasProject.Create;
    PreH2PasEdit.ListOfTools:=Project.PreH2PasTools;
    PostH2PasEdit.ListOfTools:=Project.PostH2PasTools;
  end;

  UpdateAll(false);
end;

procedure TH2PasDialog.FormCloseQuery(Sender: TObject; var CanClose: boolean);
var
  DlgResult: TModalResult;
begin
  //DebugLn(['TH2PasDialog.FormCloseQuery Converter.Modified=',Converter.Modified,' Project.Modified=',Project.Modified]);
  if Converter.Modified
  or ((Project<>nil) and (Project.Modified)) then begin
    DlgResult := QuestionDlg(h2pSaveChanges,
      h2pSaveSettings2, mtConfirmation,
      [mrYes, h2pSaveAndExit, mrNo, h2pDiscardChangesAndExit,
       mrCancel, h2pDoNotExit], 0);
    case DlgResult of
    mrYes: CanClose:=SaveSettings=mrOk;
    mrNo: ;
    else CanClose:=false;
    end;
  end;
end;

procedure TH2PasDialog.CloseButtonClick(Sender: TObject);
begin
  Close;
end;

procedure TH2PasDialog.ConvertAndBuildButtonClick(Sender: TObject);
begin
  if Convert=mrOk then
    LazarusIDE.DoBuildProject(crCompile,[]);
end;

procedure TH2PasDialog.ConvertButtonClick(Sender: TObject);
begin
  Convert;
end;

procedure TH2PasDialog.DeleteCHeadersButtonClick(Sender: TObject);
var
  DeleteFiles: TStringList;
  Node: TTreeNode;
begin
  DeleteFiles:=TStringList.Create;
  Node:=CHeaderFilesCheckTreeView.GetFirstMultiSelected;
  while Node<>nil do begin
    if Node.Parent=nil then begin
      // top lvl node is a .h file
      DeleteFiles.Add(GetNodeFilename(Node));
    end;
    Node:=Node.GetNextMultiSelected;
  end;
  if DeleteFiles.Count>0 then begin
    if QuestionDlg(h2pConfirmRemoval,
      Format(h2pDeleteTheseHFilesFromList, [#13, #13, DeleteFiles.Text]),
      mtConfirmation, [mrYes, h2pRemoveAllFiles, mrCancel], 0) = mrYes
    then begin
      Project.DeleteFiles(DeleteFiles);
    end;
    UpdateFilesPage(true);
  end;
  DeleteFiles.Free;
end;

procedure TH2PasDialog.AddCHeadersButtonClick(Sender: TObject);
var
  OpenDialog: TOpenDialog;
begin
  OpenDialog:=TOpenDialog.Create(nil);
  try
    InitIDEFileDialog(OpenDialog);
    OpenDialog.Title := h2pAddHFiles2;
    OpenDialog.Options:=OpenDialog.Options+[ofAllowMultiSelect,ofFileMustExist];
    OpenDialog.Filter := Format(h2pCHeaderFileHHAllFiles, [FileMask]);
    if OpenDialog.Execute then begin
      Project.AddFiles(OpenDialog.Files);
      UpdateFilesPage(true);
    end;
  finally
    StoreIDEFileDialog(OpenDialog);
    OpenDialog.Free;
  end;
end;

procedure TH2PasDialog.CHeaderFilesCheckTreeViewMouseDown(Sender: TOBject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Node: TTreeNode;
  StateIconLeft: LongInt;
  AFilename: String;
  CurFile: TH2PasFile;
begin
  if (Button=mbLeft) and (Shift=[]) then ;
  Node:=CHeaderFilesCheckTreeView.GetNodeAt(X,Y);
  if (Node=nil) or (Node.Parent<>nil) then exit;
  StateIconLeft:=Node.DisplayStateIconLeft;
  if (x>=StateIconLeft) and (x<StateIconLeft+FileStateImageList.Width) then
  begin
    AFilename:=GetNodeFilename(Node);
    CurFile:=Project.CHeaderFileWithFilename(AFilename);
    if CurFile=nil then exit;
    CurFile.Enabled:=not CurFile.Enabled;
    Node.StateIndex:=GetFileNodeStateIndex(CurFile);
  end;
end;

procedure TH2PasDialog.AddIncludedCHeaderFilesButtonClick(Sender: TObject);
var
  AFile: TH2PasFile;
  sl: TStringList;
  i: Integer;
  IncFile: TH2PasFileCInclude;
  CurFilename: String;
  j: Integer;
  s: String;
begin
  AFile:=GetCurrentCHeaderFile;
  if AFile=nil then exit;
  AFile.ReadCIncludes(false);
  sl:=TStringList.Create;
  try
    for i:=0 to AFile.CIncludeCount-1 do begin
      IncFile:=AFile.CIncludes[i];
      CurFilename:=IncFile.Filename;
      if CurFilename='' then continue;
      // .h file found in include directories
      if Project.CHeaderFileWithFilename(CurFilename)<>nil then continue;
      // .h file not yet in project
      j:=sl.Count-1;
      while (j>=0) and (CompareFilenames(CurFilename,sl[j])<>0) do dec(j);
      if j>=0 then continue;
      // .h file not yet in list
      sl.Add(CurFilename);
    end;
    if sl.Count>0 then begin
      s:='';
      for i:=0 to sl.Count-1 do begin
        CurFilename:=Project.ShortenFilename(sl[i]);
        s:=s+#13+CurFilename;
      end;
      if QuestionDlg(h2pAddHFiles3,
        Format(h2pAddTheseHFilesToH2pasProject, [#13, s, #13]),
        mtConfirmation,[mrYes,mrNo],0)=mrYes
      then begin
        Project.AddFiles(sl);
        Project.ReadAllCIncludes(true);
        UpdateFilesPage(false);
      end;
    end;
  finally
    sl.Free;
  end;
end;

procedure TH2PasDialog.CHeaderFilesCheckTreeViewDblClick(Sender: TObject);
var
  CurFile: TH2PasFile;
begin
  CurFile:=GetCurrentCHeaderFile;
  if CurFile<>nil then
    LazarusIDE.DoOpenEditorFile(CurFile.Filename,-1, -1,[]);
end;

procedure TH2PasDialog.CHeaderFilesCheckTreeViewSelectionChanged(Sender: TObject);
begin
  UpdateFileInfo;
end;

procedure TH2PasDialog.FormDestroy(Sender: TObject);
begin
  PreH2PasEdit.ListOfTools:=nil;
  PostH2PasEdit.ListOfTools:=nil;
  FreeAndNil(FConverter);
end;

procedure TH2PasDialog.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key=VK_ESCAPE) and (Shift=[]) then begin
    ModalResult:=mrCancel;
    Key:=VK_UNKNOWN;
  end;
  if (Key=VK_S) and (Shift=[ssCtrl]) then begin
    SaveSettings;
    Key:=VK_UNKNOWN;
  end;
end;

procedure TH2PasDialog.H2PasFilenameEditEditingDone(Sender: TObject);
begin
  Converter.h2pasFilename:=H2PasFilenameEdit.Text;
end;

procedure TH2PasDialog.LibnameEditEditingDone(Sender: TObject);
begin
  if Project<>nil then
    Project.Libname:=LibnameEdit.Text;
end;

procedure TH2PasDialog.MergeAllCHeadersExceptCurrentButtonClick(Sender: TObject);
begin
  MarkAllCHeadersExceptCurrentToMerge;
end;

procedure TH2PasDialog.MergeFileCheckBoxEditingDone(Sender: TObject);
var
  CurFile: TH2PasFile;
begin
  if Project=nil then exit;
  CurFile:=GetCurrentCHeaderFile;
  if CurFile=nil then exit;
  CurFile.Merge:=MergeFileCheckBox.Checked;
  UpdateFileInfo;
end;

procedure TH2PasDialog.MoveFileDownButtonClick(Sender: TObject);
begin
  MoveCurrentFile(1);
end;

procedure TH2PasDialog.MoveFileUpButtonClick(Sender: TObject);
begin
  MoveCurrentFile(-1);
end;

procedure TH2PasDialog.NewSettingsButtonClick(Sender: TObject);
begin
  Project.Filename:='';
  Project.Clear(true);
  UpdateAll(true);
end;

procedure TH2PasDialog.OpenLastProjectOnStartCheckBoxChange(Sender: TObject);
begin
  Converter.AutoOpenLastProject:=OpenLastProjectOnStartCheckBox.Checked;
end;

procedure TH2PasDialog.OpenSettingsButtonClick(Sender: TObject);
var
  OpenDialog: TOpenDialog;
begin
  OpenDialog:=TOpenDialog.Create(nil);
  try
    InitIDEFileDialog(OpenDialog);
    OpenDialog.Title := h2pOpenProjectH2p;
    OpenDialog.Options:=OpenDialog.Options+[ofFileMustExist];
    OpenDialog.Filter := Format(h2pH2pasProjectH2pH2pAllFiles, [FileMask]);
    if OpenDialog.Execute then begin
      OpenProject(OpenDialog.FileName,[]);
    end;
  finally
    StoreIDEFileDialog(OpenDialog);
    OpenDialog.Free;
  end;
end;

procedure TH2PasDialog.OutputDirBrowseButtonClick(Sender: TObject);
var
  ADirectory: String;
begin
  ADirectory:=OutputDirEdit.Text;
  if not ShowSelectDirDialog(h2pOutputDirectory, ADirectory) then exit;
  Project.OutputDirectory:=Project.ShortenFilename(ADirectory);
  OutputDirEdit.Text:=Project.OutputDirectory;
end;

procedure TH2PasDialog.OutputDirEditEditingDone(Sender: TObject);
begin
  Project.OutputDirectory:=OutputDirEdit.Text;
end;

procedure TH2PasDialog.OutputExtEditEditingDone(Sender: TObject);
begin
  Project.OutputExt:=OutputExtEdit.Text;
end;

procedure TH2PasDialog.PostH2PasEditModified(Sender: TObject);
begin
  Project.Modified:=true;
end;

procedure TH2PasDialog.PreH2PasEditModified(Sender: TObject);
begin
  Project.Modified:=true;
end;

procedure TH2PasDialog.SaveSettingsAsButtonClick(Sender: TObject);
begin
  SaveProject('',[sfSaveAs]);
end;

procedure TH2PasDialog.SaveSettingsButtonClick(Sender: TObject);
begin
  SaveProject('',[]);
end;

procedure TH2PasDialog.EnableAllCHeadersButtonClick(Sender: TObject);
var
  i: Integer;
begin
  CHeaderFilesCheckTreeView.BeginUpdate;
  for i:=0 to CHeaderFilesCheckTreeView.Items.TopLvlCount-1 do
    CHeaderFilesCheckTreeView.Items.TopLvlItems[i].StateIndex:=1;
  for i:=0 to Project.CHeaderFileCount-1 do
    Project.CHeaderFiles[i].Enabled:=true;
  CHeaderFilesCheckTreeView.EndUpdate;
  UpdateFileInfo;
end;

procedure TH2PasDialog.DisableAllCHeadersButtonClick(Sender: TObject);
var
  i: Integer;
begin
  CHeaderFilesCheckTreeView.BeginUpdate;
  for i:=0 to CHeaderFilesCheckTreeView.Items.TopLvlCount-1 do
    CHeaderFilesCheckTreeView.Items.TopLvlItems[i].StateIndex:=0;
  for i:=0 to Project.CHeaderFileCount-1 do
    Project.CHeaderFiles[i].Enabled:=false;
  CHeaderFilesCheckTreeView.EndUpdate;
  UpdateFileInfo;
end;

procedure TH2PasDialog.h2pasFilenameBrowseButtonClick(Sender: TObject);
var
  AFilename: String;
begin
  AFilename:=H2PasFilenameEdit.Text;
  if not ShowOpenFileDialog(h2pFilenameOfH2pasProgram, AFilename) then exit;
  Converter.h2pasFilename:=AFilename;
  H2PasFilenameEdit.Text:=Converter.h2pasFilename;
end;

procedure TH2PasDialog.h2pasOptionsCheckGroupItemClick(Sender: TObject;
  Index: LongInt);
var
  s: string;
  p: Integer;
  OptionStr: String;
  NewValue: boolean;
begin
  if Index<0 then exit;
  NewValue:=h2pasOptionsCheckGroup.Checked[Index];
  s:=h2pasOptionsCheckGroup.Items[Index];
  p:=2;
  while (p<=length(s)) and (s[p]<>' ') do inc(p);
  OptionStr:=copy(s,2,p-2);
  
  if length(OptionStr)=1 then begin
    case OptionStr[1] of
    'd': Project.UseExternal:=NewValue;
    'D': Project.UseExternalLibname:=NewValue;
    'e': Project.ConstantsInsteadOfEnums:=NewValue;
    'c': Project.CompactOutputmode:=NewValue;
    'i': Project.CreateIncludeFile:=NewValue;
    'p': Project.PforPointers:=NewValue;
    'P': Project.UseProcVarsForImport:=NewValue;
    's': Project.StripComments:=NewValue;
    'S': Project.StripCommentsAndInfo:=NewValue;
    't': Project.TforTypedefs:=NewValue;
    'T': Project.TforTypedefsRemoveUnderscore:=NewValue;
    'v': Project.VarParams:=NewValue;
    'w': Project.Win32Header:=NewValue;
    'x': Project.PalmOSSYSTrap:=NewValue;
    'C': Project.UseCTypes:=NewValue;
    else
      raise Exception.Create(Format(h2pTH2PasDialogH2pasOptionsCheckGroupItemClickUnknown, [OptionStr]));
    end;
  end else begin
    if OptionStr='pr' then
      Project.PackAllRecords:=NewValue;
  end;
end;

procedure TH2PasDialog.OnShowSrcEditSection(Sender: TObject);
var
  SrcEdit: TSourceEditorInterface;
begin
  SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
  SrcEditSection.Visible:=(SrcEdit<>nil)
                          and Converter.FileIsRelated(SrcEdit.FileName);
  //DebugLn(['TH2PasDialog.OnShowSrcEditSection ',SrcEditSection.Visible]);
end;

procedure TH2PasDialog.OnAddSearchAndReplaceBeforeH2PasClick(Sender: TObject);
var
  Tool: TCustomTextConverterTool;
  SrcEdit: TSourceEditorInterface;
  s: String;
begin
  //DebugLn(['TH2PasDialog.OnAddSearchAndReplaceBeforeH2PasClick']);
  SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
  if SrcEdit=nil then exit;
  MainPageControl.ActivePage:=PreH2PasTabSheet;
  ShowOnTop;
  // get current source editor selection or line
  s:=SrcEdit.Selection;
  if s='' then
    s:=SrcEdit.CurrentLineText;
  // add a search and replace tool
  Tool:=PreH2PasEdit.CreateTool(TTextReplaceTool);
  TTextReplaceTool(Tool).SearchFor:=s;
  if (System.Pos(#10,s)>0) or (System.Pos(#13,s)>0) then
    TTextReplaceTool(Tool).Options:=TTextReplaceTool(Tool).Options+[trtMultiLine];
  PreH2PasEdit.PropertyGrid.RefreshPropertyValues;
  //DebugLn(['TH2PasDialog.OnAddSearchAndReplaceBeforeH2PasClick ',s]);
end;

function TH2PasDialog.GetProject: TH2PasProject;
begin
  Result:=Converter.Project;
end;

procedure TH2PasDialog.UpdateAll(ScanIncludes: boolean);
begin
  UpdateCaption;
  UpdateFilesPage(ScanIncludes);
  UpdateH2PasPage;
  UpdateConvertPage;
  UpdateSettingsPage;
end;

procedure TH2PasDialog.UpdateProjectChanged(ScanIncludes: boolean);
begin
  UpdateCaption;
  UpdateFilesPage(ScanIncludes);
  UpdateH2PasPage;
  UpdateConvertPage;
end;

procedure TH2PasDialog.UpdateCaption;
var
  s: String;
begin
  s:=h2pCHeaderFileConverter;
  if Project<>nil then
    s:=s+' - '+ExtractFileNameOnly(Project.Filename);
  Caption:=s;
end;

procedure TH2PasDialog.UpdateFileInfo;
var
  AFile: TH2PasFile;
  s: String;
  Filename: String;
  OutputFilename: String;
  i: Integer;
  IncFile: TH2PasFileCInclude;
begin
  AFile:=GetCurrentCHeaderFile;
  if AFile<>nil then begin
    Filename:=AFile.Filename;
    s := Format(h2pFile, [Filename]);
    if not FileExistsCached(Filename) then
      s := s + #13 + h2pERRORFileNotFound2;
    if AFile.MergedBy<>nil then begin
      OutputFilename:=AFile.MergedBy.GetOutputFilename;
      s := s + #13 + Format(h2pMergedInto, [OutputFilename]);
    end else begin
      OutputFilename:=AFile.GetOutputFilename;
      s := s + #13 + Format(h2pOutput, [OutputFilename]);
    end;

    AFile.ReadCIncludes(false);
    if AFile.CIncludeCount>0 then begin
      s := s + #13#13 + h2pIncludes;
      for i:=0 to AFile.CIncludeCount-1 do begin
        IncFile:=AFile.CIncludes[i];
        s:=s+#13+Project.ShortenFilename(IncFile.Filename)+':'+IntToStr(IncFile.SrcPos.Y);
      end;
      AddIncludedCHeaderFilesButton.Enabled:=true;
    end else begin
      AddIncludedCHeaderFilesButton.Enabled:=false;
    end;

    if AFile.CIncludedByCount>0 then begin
      s := s + #13#13 + h2pIncludedBy;
      for i:=0 to AFile.CIncludedByCount-1 do begin
        IncFile:=AFile.CIncludedBy[i];
        s:=s+#13+Project.ShortenFilename(IncFile.Owner.Filename)+':'+IntToStr(IncFile.SrcPos.Y);
      end;
    end;

    FileInfoMemo.Caption:=s;

    MergeFileCheckBox.Checked:=AFile.Merge;
    MergeFileCheckBox.Enabled:=true;
  end else begin
    FileInfoMemo.Caption := h2pNoFileSelected;
    MergeFileCheckBox.Enabled:=false;
    AddIncludedCHeaderFilesButton.Enabled:=false;
  end;
end;

procedure TH2PasDialog.ClearMessages;
begin
  IDEMessagesWindow.Clear;
end;

procedure TH2PasDialog.CreateLazarusMenuItems;
begin
  // add a context menu to the source editor. It will be freed by ide automatically
  fSrcEditSection:=RegisterIDESubMenu(SrcEditMenuSectionFirstStatic,
                                      h2pH2pasProject, 'h2pas', nil, nil);
  fSrcEditSection.AddHandlerOnShow(@OnShowSrcEditSection);
  // add a menu item to easily create a Search and replace from the current
  // selection or line of the source editor.
  fSrcEditAddSearchReplaceMenuItem:=RegisterIDEMenuCommand(SrcEditSection,
      'Add "search and replace" tool before H2Pas',
      h2pAddSearchAndReplaceToolBeforeH2pas,
      @OnAddSearchAndReplaceBeforeH2PasClick);
end;

function TH2PasDialog.GetNodeFilename(Node: TTreeNode): string;
var
  p: LongInt;
begin
  Result:=Node.Text;
  p:=System.Pos('(',Result);
  if p>0 then
    Result:=copy(Result,1,p-2);
  Result:=Project.LongenFilename(Result);
end;

function TH2PasDialog.GetCurrentCHeaderFile: TH2PasFile;
var
  AFilename: String;
  Node: TTreeNode;
begin
  Result:=nil;
  Node:=CHeaderFilesCheckTreeView.Selected;
  if (Node=nil) or (Node.Parent<>nil) then exit;
  AFilename:=GetNodeFilename(Node);
  Result:=Project.CHeaderFileWithFilename(AFilename);
end;

procedure TH2PasDialog.MoveCurrentFile(Offset: integer);
var
  Index: LongInt;
  AFilename: String;
  NewIndex: Integer;
  Node: TTreeNode;
begin
  if Offset=0 then begin
    DebugLn(['TH2PasDialog.MoveCurrentFile Offset=0']);
    exit;
  end;
  Node:=CHeaderFilesCheckTreeView.Selected;
  if (Node=nil) or (Node.Parent<>nil) then exit;
  AFilename:=GetNodeFilename(Node);
  Index:=Project.CHeaderFileIndexWithFilename(AFilename);
  if Index<0 then begin
    DebugLn(['TH2PasDialog.MoveCurrentFile not found: Filename=',AFilename]);
    exit;
  end;
  NewIndex:=Index+Offset;
  if (NewIndex<0) or (NewIndex>=Project.CHeaderFileCount) then begin
    DebugLn(['TH2PasDialog.MoveCurrentFile out of bounds: NewIndex=',NewIndex]);
    exit;
  end;
  Project.CHeaderFileMove(Index,NewIndex);
  CHeaderFilesCheckTreeView.ConsistencyCheck;
  Node.Index:=NewIndex;
  CHeaderFilesCheckTreeView.ConsistencyCheck;
end;

function TH2PasDialog.GetFileNodeStateIndex(aFile: TH2PasFile): Integer;
begin
  if aFile=nil then
    Result:=0
  else if aFile.Enabled then
    Result:=1
  else
    Result:=0;
end;

procedure TH2PasDialog.MarkAllCHeadersExceptCurrentToMerge;
var
  CurFile: TH2PasFile;
  i: Integer;
  OtherFile: TH2PasFile;
begin
  if Project=nil then exit;
  CurFile:=GetCurrentCHeaderFile;
  if CurFile=nil then exit;
  for i:=0 to Project.CHeaderFileCount-1 do begin
    OtherFile:=Project.CHeaderFiles[i];
    OtherFile.Merge:=OtherFile<>CurFile;
  end;
  UpdateFileInfo;
end;

procedure TH2PasDialog.UpdateFilesPage(ScanIncludes: boolean);
var
  i: Integer;
  CurFile: TH2PasFile;
  OldSelection: TStringListUTF8Fast;
  s: String;
  OldExpandedState: TTreeNodeExpandedState;
  Node: TTreeNode;
  OldSelected: String;
  j: Integer;
begin
  if ScanIncludes and (Project<>nil) then
    Project.ReadAllCIncludes(false);
  CHeaderFilesCheckTreeView.BeginUpdate;
  OldSelection:=nil;
  OldExpandedState:=TTreeNodeExpandedState.Create(CHeaderFilesCheckTreeView);
  try
    // remember old selection
    OldSelected:='';
    if CHeaderFilesCheckTreeView.Selected<>nil then
      OldSelected:=CHeaderFilesCheckTreeView.Selected.Text;
    OldSelection:=TStringListUTF8Fast.Create;
    Node:=CHeaderFilesCheckTreeView.GetFirstMultiSelected;
    while Node<>nil do begin
      //DebugLn(['TH2PasDialog.UpdateFilesPage Node.Text="',Node.Text,'" Index=',Node.Index]);
      OldSelection.Add(Node.GetTextPath);
      Node:=Node.GetNextMultiSelected;
    end;
    
    // clear
    CHeaderFilesCheckTreeView.Items.Clear;
    
    // fill
    for i:=0 to Project.CHeaderFileCount-1 do begin
      CurFile:=Project.CHeaderFiles[i];
      s:=Project.ShortenFilename(CurFile.Filename);
      if CurFile.CIncludedByCount>0 then
        s := Format(h2pIncludedBy2, [s, IntToStr(CurFile.CIncludedByCount)]);
      Node:=CHeaderFilesCheckTreeView.Items.Add(nil,s);
      Node.MultiSelected:=OldSelection.IndexOf(Node.GetTextPath)>=0;
      Node.Selected:=Node.Text=OldSelected;
      Node.StateIndex:=GetFileNodeStateIndex(CurFile);
      for j:=0 to CurFile.CIncludeCount-1 do begin

      end;
    end;

    // restore expanded state
    OldExpandedState.Apply(CHeaderFilesCheckTreeView);
  finally
    OldExpandedState.Free;
    OldSelection.Free;
    CHeaderFilesCheckTreeView.EndUpdate;
  end;

  UpdateFileInfo;
end;

procedure TH2PasDialog.UpdateH2PasPage;

  procedure Check(const Option: string; NewValue: boolean);
  var
    i: Integer;
    s: string;
  begin
    for i:=0 to h2pasOptionsCheckGroup.Items.Count-1 do begin
      s:=h2pasOptionsCheckGroup.Items[i];
      if copy(s,1,length(Option)+1)=Option+' ' then
        h2pasOptionsCheckGroup.Checked[i]:=NewValue;
    end;
  end;

begin
  Check('-d',Project.UseExternal);
  Check('-D',Project.UseExternalLibname);
  Check('-e',Project.ConstantsInsteadOfEnums);
  Check('-c',Project.CompactOutputmode);
  Check('-i',Project.CreateIncludeFile);
  Check('-p',Project.PforPointers);
  Check('-pr',Project.PackAllRecords);
  Check('-P',Project.UseProcVarsForImport);
  Check('-s',Project.StripComments);
  Check('-S',Project.StripCommentsAndInfo);
  Check('-t',Project.TforTypedefs);
  Check('-T',Project.TforTypedefsRemoveUnderscore);
  Check('-v',Project.VarParams);
  Check('-w',Project.Win32Header);
  Check('-x',Project.PalmOSSYSTrap);
  Check('-C',Project.UseCTypes);
  
  LibnameEdit.Text:=Project.Libname;
  OutputExtEdit.Text:=Project.OutputExt;
  OutputDirEdit.Text:=Project.OutputDirectory;
end;

procedure TH2PasDialog.UpdateConvertPage;
begin
  ClearMessages;
  PreH2PasEdit.ListOfTools:=Project.PreH2PasTools;
  PreH2PasEdit.UpdateAll;
  PostH2PasEdit.ListOfTools:=Project.PostH2PasTools;
  PostH2PasEdit.UpdateAll;
  //DebugLn(['TH2PasDialog.UpdateConvertPage PreH2PasEdit.ListOfTools=',PreH2PasEdit.ListOfTools.COmponentCount]);
end;

procedure TH2PasDialog.UpdateSettingsPage;
begin
  H2PasFilenameEdit.Text:=Converter.h2pasFilename;
  OpenLastProjectOnStartCheckBox.Checked:=Converter.AutoOpenLastProject;
end;

function TH2PasDialog.ShowSelectDirDialog(const Title: string;
  var ADirectory: string): boolean;
var
  SelectDirDialog: TSelectDirectoryDialog;
begin
  Result:=false;
  SelectDirDialog:=TSelectDirectoryDialog.Create(nil);
  try
    InitIDEFileDialog(SelectDirDialog);
    SelectDirDialog.Title:=Title;
    if (ADirectory<>'')
    and (FilenameIsAbsolute(ADirectory)) then
      SelectDirDialog.InitialDir:=ADirectory;
    if SelectDirDialog.Execute then begin
      ADirectory:=SelectDirDialog.FileName;
      Result:=true;
    end;
  finally
    StoreIDEFileDialog(SelectDirDialog);
    SelectDirDialog.Free;
  end;
end;

function TH2PasDialog.ShowOpenFileDialog(const Title: string;
  var AFilename: string): boolean;
var
  OpenDialog: TOpenDialog;
begin
  Result:=false;
  OpenDialog:=TOpenDialog.Create(nil);
  try
    InitIDEFileDialog(OpenDialog);
    OpenDialog.Title:=Title;
    if (AFilename<>'')
    and (FilenameIsAbsolute(AFilename)) then
      OpenDialog.InitialDir:=ExtractFilePath(AFilename);
    if AFilename<>'' then
      OpenDialog.Filename:=ExtractFileName(AFilename);
    if OpenDialog.Execute then begin
      AFilename:=OpenDialog.FileName;
      Result:=true;
    end;
  finally
    StoreIDEFileDialog(OpenDialog);
    OpenDialog.Free;
  end;
end;

function TH2PasDialog.OnIDESavedAll(Sender: TObject): TModalResult;
begin
  Result:=SaveSettings;
end;

function TH2PasDialog.Convert: TModalResult;
begin
  Result:=mrCancel;

  if not Project.HasEnabledFiles then begin
    IDEMessageDialog(h2pNothingToDo,
      h2pPleaseEnableAtLeastOneCHeaderFileThatIsNotMerged,
      mtInformation,[mbOk],'');
    Result:=mrOK;
    exit;
  end;
  
  // save settings
  if SaveSettings<>mrOk then begin
    DebugLn(['TH2PasDialog.Convert SaveSettings failed']);
    exit;
  end;
  
  // save IDE files
  if LazarusIDE.DoSaveProject([])<>mrOK then begin
    DebugLn(['TH2PasDialog.Convert LazarusIDE.DoSaveProject failed']);
    exit;
  end;
  LazarusIDE.SaveSourceEditorChangesToCodeCache(nil);
  
  Result:=Converter.Execute;
  if Result<>mrOk then begin
    ShowH2PasError;
  end;
end;

procedure TH2PasDialog.ShowH2PasError;
begin
  LazarusIDE.DoJumpToCompilerMessage(true);
end;

function TH2PasDialog.SaveSettings: TModalResult;
begin
  Result:=SaveGlobalSettings;
  if Result<>mrOk then exit;
  if (Project<>nil) and Project.Modified then
    Result:=SaveProject('',[]);
end;

function TH2PasDialog.SaveGlobalSettings: TModalResult;
var
  Config: TConfigStorage;
begin
  Result:=mrCancel;
  try
    Config:=GetIDEConfigStorage('h2pastool.xml',false);
    try
      Converter.WindowBounds:=BoundsRect;
      Converter.Save(Config);
      Config.WriteToDisk;
    finally
      Config.Free;
    end;
    Result:=mrOk;
  except
    on E: Exception do begin
      MessageDlg(h2pWriteError,
        Format(h2pErrorWritingGlobalConfig, [#13, E.Message]), mtError, [mbCancel], 0);
    end;
  end;
end;

function TH2PasDialog.LoadGlobalSettings: TModalResult;
var
  Config: TConfigStorage;
begin
  Result:=mrCancel;
  try
    Config:=GetIDEConfigStorage('h2pastool.xml',true);
    try
      Converter.Load(Config);
      if (Converter.WindowBounds.Left>-10)
      and (Converter.WindowBounds.Right<Screen.Width+10)
      and (Converter.WindowBounds.Top>-10)
      and (Converter.WindowBounds.Bottom<Screen.Height+10)
      and (Converter.WindowBounds.Right-Converter.WindowBounds.Left>100)
      and (Converter.WindowBounds.Bottom-Converter.WindowBounds.Top>50)
      then
        BoundsRect:=Converter.WindowBounds;
      UpdateSettingsPage;
    finally
      Config.Free;
    end;
    Result:=mrOk;
  except
    on E: Exception do begin
      MessageDlg(h2pReadError,
        Format(h2pErrorReadingGlobalConfig, [#13, E.Message]), mtError, [mbCancel], 0);
    end;
  end;
end;

function TH2PasDialog.SaveProject(const Filename: string; Flags: TSaveFlags
  ): TModalResult;
var
  NewFilename: String;
  SaveDialog: TSaveDialog;
  NewPath: String;
begin
  Result:=mrCancel;
  if Project=nil then exit(mrOk);
  NewFilename:=Filename;
  if NewFilename='' then
    NewFilename:=Project.Filename;
    
  // choose a filename
  if (not FilenameIsAbsolute(NewFilename))
  or (sfSaveAs in Flags) then begin
    SaveDialog:=TSaveDialog.Create(nil);
    try
      InitIDEFileDialog(SaveDialog);
      SaveDialog.Title := h2pSaveProjectAsH2p;
      //choose a nice default name
      if NewFilename='' then
        NewFilename:='project1.h2p';
      SaveDialog.FileName:=NewFilename;
      SaveDialog.Filter := Format(h2pH2pasProjectH2pH2pAllFiles, [FileMask]);
      NewPath:=ExtractFilePath(NewFilename);
      if NewPath<>'' then
        SaveDialog.InitialDir:=NewPath;
      if (not SaveDialog.Execute) then exit;
      NewFilename:=SaveDialog.FileName;
      if NewFilename='' then exit;
      // append default file extension
      if ExtractFileExt(NewFilename)='' then
        NewFilename:=NewFilename+'.h2p';
      // warn if overwriting
      if FileExistsUTF8(NewFilename) then begin
        if QuestionDlg(h2pReplaceFile,
          Format(h2pTheFileAlreadyExists, [NewFilename, #13]),
          mtConfirmation, [mrOk, h2pOverwrite, mrCancel, h2pCancel], 0) <> mrOk
        then exit;
      end;
    finally
      StoreIDEFileDialog(SaveDialog);
      SaveDialog.Free;
    end;
  end;
  
  // save
  try
    DebugLn(['TH2PasDialog.SaveProject saving project']);
    Converter.SaveProject(NewFilename);
    Result:=mrOk;
  except
    on E: Exception do begin
      MessageDlg(h2pWriteError,
        Format(h2pErrorWritingGlobalConfig, [#13, E.Message]), mtError, [mbCancel], 0);
    end;
  end;
  UpdateCaption;
end;

function TH2PasDialog.OpenProject(const Filename: string; Flags: TOpenFlags
  ): TModalResult;
var
  NewFilename: String;
begin
  Result:=mrCancel;
  NewFilename:=ExpandFileNameUTF8(TrimFilename(Filename));
  if not FileExistsUTF8(NewFilename) then begin
    if ofOnlyIfExists in Flags then begin
      MessageDlg(h2pFileNotFound,
        Format(h2pFileNotFound2, [NewFilename]), mtError, [mbCancel], 0);
      exit;
    end;
  end;
  
  // create new project
  if Project=nil then
    Converter.Project:=TH2PasProject.Create;

  if FileExistsUTF8(NewFilename) then begin
    // load
    try
      Converter.LoadProject(NewFilename);
      Result:=mrOk;
    except
      on E: Exception do begin
        MessageDlg(h2pReadError,
          Format(h2pErrorReadingProjectFromFile, [#13, NewFilename, #13, #13, E.Message]), mtError, [mbCancel], 0);
      end;
    end;
  end else begin
    // new project
    Project.Clear(true);
    Converter.CurrentProjectFilename:=NewFilename;
    Project.Filename:=NewFilename;
  end;
  
  UpdateProjectChanged(true);
end;

end.


