unit Main;

{$include lazhexeditor.inc}

interface

uses
{$IFDEF FPC}
  LCLType, LCLIntf, LResources,
{$ELSE}
  Windows, Messages,
{$ENDIF}
  SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ActnList, ComCtrls, StdCtrls, Menus, ToolWin, ExtCtrls, ImgList,
  KEditCommon, KHexEditor, KControls, KDialogs;


type

  { TMainForm }

  TMainForm = class(TForm)
    MainMenu: TMainMenu;
    MGFile: TMenuItem;
    MINew: TMenuItem;
    MIOpen: TMenuItem;
    MISave: TMenuItem;
    MISaveAs: TMenuItem;
    MIClose: TMenuItem;
    MIExit: TMenuItem;
    N1: TMenuItem;
    StatusBar: TStatusBar;
    ALFile: TActionList;
    ACNew: TAction;
    ACOpen: TAction;
    ACSave: TAction;
    ACSaveAs: TAction;
    ACClose: TAction;
    ACExit: TAction;
    ODMain: TOpenDialog;
    SDMain: TSaveDialog;
    ACStatusBar: TAction;
    ALEdit: TActionList;
    ACUndo: TAction;
    ACRedo: TAction;
    ACCut: TAction;
    ACCopy: TAction;
    ACPaste: TAction;
    ACDelete: TAction;
    ACSelectAll: TAction;
    MGEdit: TMenuItem;
    MIUndo: TMenuItem;
    MIRedo: TMenuItem;
    N2: TMenuItem;
    MICut: TMenuItem;
    MICopy: TMenuItem;
    MIPaste: TMenuItem;
    MIDelete: TMenuItem;
    N3: TMenuItem;
    MISelectAll: TMenuItem;
    MGSearch: TMenuItem;
    MGOptions: TMenuItem;
    MISearch: TMenuItem;
    MIReplace: TMenuItem;
    MISearchAgain: TMenuItem;
    MIOptions: TMenuItem;
    MGHelp: TMenuItem;
    MIAbout: TMenuItem;
    ALSearch: TActionList;
    ACSearch: TAction;
    ACReplace: TAction;
    ACSearchAgain: TAction;
    ACEditorOptions: TAction;
    MIMRUFEnd: TMenuItem;
    MIMRUF1: TMenuItem;
    MIMRUF2: TMenuItem;
    MIMRUF3: TMenuItem;
    MIMRUF4: TMenuItem;
    MIMRUF5: TMenuItem;
    PMMRUFs: TPopupMenu;
    PMIMRUF1: TMenuItem;
    PMIMRUF2: TMenuItem;
    PMIMRUF3: TMenuItem;
    PMIMRUF4: TMenuItem;
    PMIMRUF5: TMenuItem;
    MIPrint: TMenuItem;
    PMMain: TPopupMenu;
    PMIUndo: TMenuItem;
    PMIRedo: TMenuItem;
    N6: TMenuItem;
    PMICut: TMenuItem;
    PMICopy: TMenuItem;
    PMIPaste: TMenuItem;
    PMIDelete: TMenuItem;
    PMISelectAll: TMenuItem;
    N7: TMenuItem;
    N8: TMenuItem;
    ACPrint: TAction;
    ACViewToolBar: TAction;
    MIViewToolBar: TMenuItem;
    PNMain: TPanel;
    N11: TMenuItem;
    MIMRUFStart: TMenuItem;
    PMIEnvProps: TMenuItem;
    ACEnabledEditor: TAction;
    ACReadOnlyEditor: TAction;
    EnabledEditor1: TMenuItem;
    Close1: TMenuItem;
    ILMain: TImageList;
    Editor: TKHexEditor;
    ACPreview: TAction;
    MIPreview: TMenuItem;
    PSDMain: TKPrintSetupDialog;
    PPDMain: TKPrintPreviewDialog;
    ToBMain: TToolBar;
    TBNew: TToolButton;
    TBOpen: TToolButton;
    TBSave: TToolButton;
    TBPreview: TToolButton;
    TBPrint: TToolButton;
    TBSep1: TToolButton;
    TBCut: TToolButton;
    TBCopy: TToolButton;
    TBPaste: TToolButton;
    TBDelete: TToolButton;
    TBSep2: TToolButton;
    TBUndo: TToolButton;
    TBRedo: TToolButton;
    TBSep3: TToolButton;
    TBFind: TToolButton;
    TBReplace: TToolButton;
    TBSearchAgain: TToolButton;
    TBSep4: TToolButton;
    TBEnvProps: TToolButton;
    ILMainDis: TImageList;
    procedure EditorReplaceText(Sender: TObject; const TextToFind,
      TextToReplace: string; var Action: TKEditReplaceAction);
    procedure FormCreate(Sender: TObject);
    procedure ACNewExecute(Sender: TObject);
    procedure ACEnabledUpdate(Sender: TObject);
    procedure ACOpenExecute(Sender: TObject);
    procedure ACSaveExecute(Sender: TObject);
    procedure ACSaveUpdate(Sender: TObject);
    procedure ACSaveAsExecute(Sender: TObject);
    procedure ACCloseExecute(Sender: TObject);
    procedure ACExitExecute(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure ACStatusBarUpdate(Sender: TObject);
    procedure ACUndoExecute(Sender: TObject);
    procedure ACUndoUpdate(Sender: TObject);
    procedure ACRedoExecute(Sender: TObject);
    procedure ACRedoUpdate(Sender: TObject);
    procedure ACCutExecute(Sender: TObject);
    procedure ACCutUpdate(Sender: TObject);
    procedure ACCopyExecute(Sender: TObject);
    procedure ACPasteExecute(Sender: TObject);
    procedure ACPasteUpdate(Sender: TObject);
    procedure ACDeleteExecute(Sender: TObject);
    procedure ACSelectAllExecute(Sender: TObject);
    procedure ACSelectAllUpdate(Sender: TObject);
    procedure ACSearchExecute(Sender: TObject);
    procedure ACSearchAgainExecute(Sender: TObject);
    procedure ACSearchAgainUpdate(Sender: TObject);
    procedure ACSearchUpdate(Sender: TObject);
    procedure ACReplaceExecute(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ACEditorOptionsExecute(Sender: TObject);
    procedure ACPrintExecute(Sender: TObject);
    procedure ACPrintUpdate(Sender: TObject);
    procedure MIAboutClick(Sender: TObject);
    procedure ACViewToolBarExecute(Sender: TObject);
    procedure ACSaveAsUpdate(Sender: TObject);
    procedure ACCloseUpdate(Sender: TObject);
    procedure ACCopyUpdate(Sender: TObject);
    procedure ACDeleteUpdate(Sender: TObject);
    procedure ACReplaceUpdate(Sender: TObject);
    procedure EditorDropFiles(Sender: TObject; X, Y: Integer;
      Files: TStrings);
    procedure ACEnabledEditorExecute(Sender: TObject);
    procedure ACEnabledEditorUpdate(Sender: TObject);
    procedure ACReadOnlyEditorExecute(Sender: TObject);
    procedure ACReadOnlyEditorUpdate(Sender: TObject);
    procedure ACViewToolBarUpdate(Sender: TObject);
    procedure EditorPrintNotify(Sender: TObject; Status: TKPrintStatus;
      var Abort: Boolean);
    procedure EditorPrintPaint(Sender: TObject);
    procedure ACPreviewExecute(Sender: TObject);
    procedure ACPreviewUpdate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FMRUFList: TStringList;
    FLastFileName: string;
    IsNew: Boolean;
    SearchData: TKEditSearchData;
    SearchAgainEnabled: Boolean;
    LastSearchCommand: TKEditCommand;
    TextToFind, TextToReplace: string;
    procedure AddMRUF(const FileName: string);
    procedure DeleteMRUF(const FileName: string);
    procedure DeleteMRUFs;
    procedure MRUFClick(Sender: TObject);
    procedure UpdateMRUFs;
    procedure LoadFromIni;
    procedure SaveToIni;
    procedure EnvironmentChanged;
    procedure CloseFile;
    procedure NewFile;
    procedure OpenFile(FileName: string);
    function SaveFile(SaveAs, NeedAnotherOp: Boolean): Boolean;
    procedure Search(Command: TKEditCommand);
    procedure HandleSearchError;
  {$IFDEF FPC}
    procedure LazDropFiles(Sender: TObject; const FileNames: Array of String);
  {$ENDIF}
  end;

var
  MainForm: TMainForm;

implementation

uses
  IniFiles, Res, Basic, About, Options, KFunctions,
  Replace, ReplacePrompt, Search, PrintStatus, ClipBrd;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.Title := AppName;
{$IFDEF FPC}
  Application.OnDropFiles := LazDropFiles;
{$ENDIF}
  Caption := AppName;
  FLastFileName := '';
  SDMain.Filter := ODMain.Filter;
  FMRUFList := TStringList.Create;
  InitEnvironment(Environment);
  InitColors(Colors);
  SearchData := DefaultSearchData;
  LoadFromIni;
  EnvironmentChanged;
  UpdateMRUFs;
  SearchAgainEnabled := False;
  if ParamCount > 0 then
    FLastFileName := ParamStr(1);
  if FLastFileName <> '' then
    OpenFile(FLastFileName)
  else
    NewFile;
  //Editor.AddressOffset := 16384;
  Editor.AreaSpacing := 1;
  Editor.DoubleBuffered := True;  
  //define 1 cm space for custom header
  Editor.PageSetup.HeaderSpace := 1; //cm by default
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  SaveToIni;
  CloseFile;
  FMRUFList.Free;
end;

procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := SaveFile(False, True);
end;

procedure TMainForm.ACNewExecute(Sender: TObject);
begin
  NewFile;
end;

procedure TMainForm.ACEnabledUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := True;
  StatusBar.Panels[4].Text := Application.Hint;
end;

procedure TMainForm.ACOpenExecute(Sender: TObject);
begin
  OpenFile('');
end;

procedure TMainForm.ACSaveExecute(Sender: TObject);
begin
  SaveFile(False, False);
end;

procedure TMainForm.ACSaveUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.Visible and Editor.Modified;
end;

procedure TMainForm.ACSaveAsExecute(Sender: TObject);
begin
  SaveFile(True, False);
end;

procedure TMainForm.ACSaveAsUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.Visible;
end;

procedure TMainForm.ACCloseExecute(Sender: TObject);
begin
  if SaveFile(False, True) then
    CloseFile;
end;

procedure TMainForm.ACCloseUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.Visible;
end;

procedure TMainForm.ACPreviewExecute(Sender: TObject);
begin
  PPDMain.Show;
end;

procedure TMainForm.ACPreviewUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := True;
end;

procedure TMainForm.ACPrintExecute(Sender: TObject);
begin
  Editor.PageSetup.Title := FLastFileName;
  PSDMain.SelAvail := Editor.SelAvail;
  PSDMain.Execute;
end;

procedure TMainForm.ACPrintUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CanPrint;
end;

procedure TMainForm.ACExitExecute(Sender: TObject);
begin
  Close;
end;

procedure TMainForm.ACStatusBarUpdate(Sender: TObject);
var
  I: Integer;
  S: string;
begin
  with StatusBar do
  begin
    if Editor.Visible then
    begin
      if Editor.AddressMode = eamDec then S := sStatusPosDec else S := sStatusPosHex;
      Panels[0].Text := Format(S, [Editor.SelEnd.Index]);
      Panels[1].Text := Format(sStatusDigit, [Editor.SelEnd.Digit]);
      Panels[2].Text := Modified2Text(Editor.Modified);
      Panels[3].Text := InsertMode2Text(Editor.InsertMode);
    end else
      for I := 0 to 3 do Panels[I].Text := '';
  end;
end;

procedure TMainForm.ACUndoExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecUndo);
end;

procedure TMainForm.ACUndoUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecUndo);
end;

procedure TMainForm.ACRedoExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecRedo);
end;

procedure TMainForm.ACRedoUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecRedo);
end;

procedure TMainForm.ACCutExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecCut);
end;

procedure TMainForm.ACCutUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecCut);
end;

procedure TMainForm.ACCopyExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecCopy);
end;

procedure TMainForm.ACCopyUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecCopy);
end;

procedure TMainForm.ACPasteExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecPaste);
end;

procedure TMainForm.ACPasteUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecPaste);
end;

procedure TMainForm.ACDeleteExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecClearSelection);
end;

procedure TMainForm.ACDeleteUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecClearSelection);
end;

procedure TMainForm.ACSelectAllExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(ecSelectAll);
end;

procedure TMainForm.ACSelectAllUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecSelectAll);
end;

procedure TMainForm.ACSearchExecute(Sender: TObject);
begin
  Search(ecSearch);
end;

procedure TMainForm.ACSearchUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecSearch);
end;

procedure TMainForm.ACReplaceExecute(Sender: TObject);
begin
  Search(ecReplace);
end;

procedure TMainForm.ACReplaceUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := Editor.CommandEnabled(ecReplace);
end;

procedure TMainForm.ACSearchAgainExecute(Sender: TObject);
begin
  Editor.ExecuteCommand(LastSearchCommand, @SearchData);
  HandleSearchError;
end;

procedure TMainForm.ACSearchAgainUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := SearchAgainEnabled;
end;

procedure TMainForm.ACEditorOptionsExecute(Sender: TObject);
begin
  OptionsForm.SetData(Environment, Colors);
  if OptionsForm.ShowModal = mrOk then
  begin
    OptionsForm.GetData(Environment, Colors);
    EnvironmentChanged;
  end;
end;

procedure TMainForm.ACViewToolBarExecute(Sender: TObject);
begin
  ACViewToolBar.Checked := not ACViewToolBar.Checked;
  ToBMain.Visible := ACViewToolBar.Checked;
end;

procedure TMainForm.ACViewToolBarUpdate(Sender: TObject);
begin
  TAction(Sender).Checked := ToBMain.Visible;
end;

procedure TMainForm.ACEnabledEditorExecute(Sender: TObject);
begin
  ACEnabledEditor.Checked := not ACEnabledEditor.Checked;
  Editor.Enabled := ACEnabledEditor.Checked;
end;

procedure TMainForm.ACEnabledEditorUpdate(Sender: TObject);
begin
  TAction(Sender).Checked := Editor.Enabled;
end;

procedure TMainForm.ACReadOnlyEditorExecute(Sender: TObject);
begin
  ACReadOnlyEditor.Checked := not ACReadOnlyEditor.Checked;
  Editor.ReadOnly := ACReadOnlyEditor.Checked;
end;

procedure TMainForm.ACReadOnlyEditorUpdate(Sender: TObject);
begin
  TAction(Sender).Checked := Editor.ReadOnly;
end;

procedure TMainForm.MIAboutClick(Sender: TObject);
begin
  AboutForm.ShowModal;
end;

procedure TMainForm.AddMRUF(const FileName: string);
begin
  if FileName <> '' then
  try
    if FMRUFList.IndexOf(FileName) < 0 then
      FMRUFList.Insert(0, FileName);
    UpdateMRUFs;
  except
  end;
end;

procedure TMainForm.DeleteMRUF(const FileName: string);
var
  I: Integer;
begin
  if FileName <> '' then
  try
    I := FMRUFList.IndexOf(FileName);
    if I >= 0 then FMRUFList.Delete(I);
    UpdateMRUFs;
  except
  end;
end;

procedure TMainForm.DeleteMRUFs;
begin
  try
    FMRUFList.Clear;
    UpdateMRUFs;
  except
  end;
end;

procedure TMainForm.MRUFClick(Sender: TObject);
var
  S: string;
begin
  S := TMenuItem(Sender).Caption;
  Delete(S, 1, 3);
  if S <> '' then
    OpenFile(S);
end;

procedure TMainForm.UpdateMRUFs;
var
  I, Index: Integer;
  MI, PMI: TMenuItem;
  S: string;
  B: Boolean;
begin
  B := False;
  Index := MGFile.IndexOf(MIMRUF1);
  for I := 0 to 4 do
  begin
    MI := MGFile.Items[I + Index];
    PMI := PMMRUFs.Items[I];
    if I < FMRUFList.Count then
    begin
      S := Format('&%d %s', [I + 1, FMRUFList[I]]);
      MI.Visible := True;
      PMI.Visible := True;
      MI.Caption := S;
      PMI.Caption := S;
      MI.OnClick := MRUFClick;
      PMI.OnClick := MRUFClick;
      B := True;
    end else
    begin
      MI.Visible := False;
      PMI.Visible := False;
    end;
  end;
  MIMRUFEnd.Visible := B;
end;

procedure TMainForm.CloseFile;
begin
  if Editor.Visible then
  begin
    Editor.Clear;
    AddMRUF(FLastFileName);
    FLastFileName := '';
    Editor.Visible := False;
  end;
end;

procedure TMainForm.NewFile;
begin
  if SaveFile(False, True) then
  begin
    CloseFile;
    IsNew := True;
    Caption := Format('%s - %s', [AppName, sNoname]);
    Editor.Visible := True;
  end;
end;

procedure TMainForm.OpenFile(FileName: string);
begin
  if SaveFile(False, True) then
  begin
    if FileName = '' then
      if ODMain.Execute then
        FileName := ODMain.FileName;
    if FileName <> '' then
    begin
      CloseFile;
      if ExtractFileDir(FileName) = '' then
        FileName := Format('%s\%s', [GetCurrentDir, FileName]);
      try
        Editor.LoadFromFile(FileName);
        FLastFileName := FileName;
        IsNew := False;
        Caption := Format('%s - %s', [AppName, FLastFileName]);
        Editor.Visible := True;
      except
        MessageBox(Handle, PChar(Format(sErrOpenError, [FileName])), PChar(sAppName), MB_OK);
      end;
      DeleteMRUF(FileName);
    end;
  end;
end;

function TMainForm.SaveFile(SaveAs, NeedAnotherOp: Boolean): Boolean;
var
  NeedDlg: Boolean;
  FileName: string;
begin
  Result := False;
  if IsNew then
    FileName := sNoname
  else
    FileName := ExtractFileName(FLastFileName);
  if NeedAnotherOp then
  begin
    if Editor.Modified then
    begin
      case MessageBox(Handle, PChar(Format(sNeedSaveQuestion, [FileName])), PChar(sAppName), MB_YESNOCANCEL) of
        ID_NO: begin Result := True; Exit end;
        ID_CANCEL: Exit;
      end
    end else
    begin
      Result := True;
      Exit;
    end;
  end;
  NeedDlg := IsNew or SaveAs;
  SDMain.FileName := FileName;
  if not NeedDlg or SDMain.Execute then
  begin
    if NeedDlg then FLastFileName := SDMain.Filename;
    try
      Editor.SaveToFile(FLastFileName);
      Editor.Modified := False;
      IsNew := False;
      Caption := Format('%s - %s', [AppName, FLastFileName]);
      Result := True;
    except
      MessageBox(Handle, PChar(Format(sErrSaveError, [FileName])), PChar(sAppName), MB_OK);
    end;
  end;
  if Result then Editor.Modified := False;
end;

procedure TMainForm.LoadFromIni;
var
  Ini: TIniFile;
  I: Integer;
  S: string;
begin
  Ini := TIniFile.Create(IniPath);
  try
    I := Ini.ReadInteger(secSettings, 'Version', 0);
    if I = IniVersion then with Environment do
    begin
      DataToString(@P, SizeOf(TEnvironmentPacked), S);
      S := Ini.ReadString(secSettings, 'Environment', S);
      StringToData(S, @P, SizeOf(TEnvironmentPacked));
      AddressPrefix := Ini.ReadString(secSettings, 'Address prefix', AddressPrefix);
      FontName := Ini.ReadString(secSettings, 'Font name', FontName);
    end;
    FLastFileName := Ini.ReadString(secSettings, 'Last file name', FLastFileName);
    WindowState := TWindowState(Ini.ReadInteger(secSettings, 'Window state', Integer(WindowState)));
    BoundsRect := Rect(
      Ini.ReadInteger(secSettings, 'Left', Left),
      Ini.ReadInteger(secSettings, 'Top', Top),
      Ini.ReadInteger(secSettings, 'Right', Left + Width),
      Ini.ReadInteger(secSettings, 'Bottom', Top + Height));
    FMRUFList.Clear;
    for I := 1 to 5 do
    begin
      S := Ini.ReadString(secMRUFs, Format('MRUF%d', [I]), '');
      if S <> '' then FMRUFList.Add(S);
    end;
  finally
    Ini.Free;
  end;
end;

procedure TMainForm.SaveToIni;
var
  Ini: TIniFile;
  I: Integer;
  S: string;
begin
  Ini := TIniFile.Create(IniPath);
  try
    Ini.WriteInteger(secSettings, 'Version', IniVersion);
    with Environment do
    begin
      DataToString(@P, SizeOf(TEnvironmentPacked), S);
      Ini.WriteString(secSettings, 'Environment', S);
      Ini.WriteString(secSettings, 'Address prefix', AddressPrefix);
      Ini.WriteString(secSettings, 'Font name', FontName);
    end;
    Ini.WriteString(secSettings, 'Last file name', FLastFileName);
    Ini.WriteInteger(secSettings, 'Window state', Integer(WindowState));
    Ini.WriteInteger(secSettings, 'Left', Left);
    Ini.WriteInteger(secSettings, 'Top', Top);
    Ini.WriteInteger(secSettings, 'Right', Left + Width);
    Ini.WriteInteger(secSettings, 'Bottom', Top + Height);
    for I := 0 to FMRUFList.Count - 1 do
      Ini.WriteString(secMRUFs, Format('MRUF%d', [I + 1]), FMRUFList[I]);
  finally
    Ini.Free;
  end;
end;

procedure TMainForm.EnvironmentChanged;
var
  Options: TKEditOptions;
  DrawStyles: TKHexEditorDrawStyles;
begin
  with Environment do
  begin
    Options := [];
    if P.DropFiles then Include(Options, eoDropFiles);
    if P.GroupUndo then Include(Options, eoGroupUndo);
    if P.UndoAfterSave then Include(Options, eoUndoAfterSave);
    Editor.Options := Options;
    DrawStyles := [];
    if P.ShowAddress then Include(DrawStyles, edAddress);
    if P.ShowDigits then Include(DrawStyles, edDigits);
    if P.ShowText then Include(DrawStyles, edText);
    if P.ShowHorzLines then Include(DrawStyles, edHorzLines);
    if P.ShowVertLines then Include(DrawStyles, edVertLines);
    if P.ShowSeparators then Include(DrawStyles, edSeparators);
    if P.ShowInactiveCaret then Include(DrawStyles, edInactiveCaret);
    Editor.DrawStyles := DrawStyles;
    Editor.AddressMode := TKHexEditorAddressMode(P.AddressMode);
    Editor.AddressPrefix := AddressPrefix;
    Editor.AddressSize := P.AddressSize;
    Editor.CharSpacing := P.CharSpacing;
    Editor.DigitGrouping := P.DigitGrouping;
    Editor.DisabledDrawStyle := TKHexEditorDisabledDrawStyle(P.DisabledDrawStyle);
    Editor.LineHeightPercent := P.LineHeightPercent;
    Editor.LineSize := P.LineSize;
    Editor.UndoLimit := P.UndoLimit;
    // font
    Editor.Font.Name := FontName;
    Editor.Font.Size := P.FontSize;
    Editor.Font.Style := P.FontStyle;
    // colors
    Editor.Colors.Colors := Colors;
  end;
end;

procedure TMainForm.Search(Command: TKEditCommand);
var
  Form: TSearchForm;
  MResult: TModalResult;
begin
  if Command = ecReplace then
    Form := ReplaceForm
  else
    Form := SearchForm;
  if Editor.EditArea = eaDigits then
    Include(SearchData.Options, esoTreatAsDigits)
  else
    Exclude(SearchData.Options, esoTreatAsDigits);
  Form.SetData(SearchData, Editor.SelAvail);
  MResult := Form.ShowModal;
  if MResult in [mrYes, mrYesToAll] then
  begin
    Form.GetData(SearchData);
    SearchAgainEnabled := True;
    LastSearchCommand := Command;
    Include(SearchData.Options, esoFirstSearch);
    TextToFind := TrimToSize(SearchData.TextToFind, 40);
    TextToReplace := TrimToSize(SearchData.TextToReplace, 40);
    ReplacePromptForm.LBText.Caption := Format(sReplaceQuestion, [TextToFind]);
    if MResult = mrYesToAll then
      Include(SearchData.Options, esoAll);
    Editor.ExecuteCommand(Command, @SearchData);
    HandleSearchError;
  end;
end;

procedure TMainForm.EditorReplaceText(Sender: TObject; const TextToFind,
  TextToReplace: String; var Action: TKEditReplaceAction);
begin
  case ReplacePromptForm.ShowModal of
    mrYes: Action := eraYes;
    mrNo: Action := eraNo;
    mrYesToAll: Action := eraAll;
    mrCancel: Action := eraCancel;
  end;
end;

procedure TMainForm.HandleSearchError;
var
  S: string;
begin
  case SearchData.ErrorReason of
    eseNoMatch: S := Format(sErrTextNotFound, [TextToFind]);
    eseNoDigitsFind: S := Format(sErrNoDigitsInText, [TextToFind]);
    eseNoDigitsReplace: S := Format(sErrNoDigitsInText, [TextToReplace]);
  else
    S := '';
  end;
  if S <> '' then
    MessageBox(Handle, PChar(S), PChar(sAppName), MB_OK);
end;

{$IFDEF FPC}
procedure TMainForm.LazDropFiles(Sender: TObject;
  const FileNames: array of String);
begin
  if Length(FileNames) > 0 then
  begin
    Application.BringToFront;
    OpenFile(FileNames[0]);
  end;
end;
{$ENDIF}

procedure TMainForm.EditorDropFiles(Sender: TObject; X, Y: Integer;
  Files: TStrings);
begin
  //not called in Lazarus but keep assigned in this demo
  if Files.Count > 0 then
  begin
    Application.BringToFront;
    OpenFile(Files[0]);
  end;
end;

procedure TMainForm.EditorPrintNotify(Sender: TObject;
  Status: TKPrintStatus; var Abort: Boolean);
begin
  with PrintStatusForm do
  begin
    case Status of
      epsBegin:
      begin
        Self.Enabled := False;
        Aborted := False;
        Show;
      end;
      epsNewPage:
      begin
        LBPage.Caption := Format(sPrintedPageAndCopy,
          [Editor.PageSetup.CurrentPage, Editor.PageSetup.PageCount,
           Editor.PageSetup.CurrentCopy, Editor.PageSetup.Copies]);
        Update;
      end;
      epsEnd:
      begin
         Hide;
         Self.Enabled := True;
      end;
    end;
    Abort := Aborted;
  end;
end;

procedure TMainForm.EditorPrintPaint(Sender: TObject);
var
  Y: Integer;
begin
  with Editor.PageSetup do
  begin
    // paint filename as header - we have 1 cm space defined
    Canvas.Brush.Style := bsClear;
    Canvas.Font.Pitch := fpDefault;
    Canvas.Font.Name := 'Arial';
    Canvas.Font.Height := VMap(16);
    Canvas.Font.Style := [fsBold];
    Canvas.Font.Color := clBlack;
    Canvas.TextOut(PrinterMarginLeftMirrored, PrinterMarginTop, FLastFileName);
    Canvas.Brush.Style := bsSolid;
    Canvas.Pen.Color := clBlack;
    Canvas.Pen.Width := VMap(2);
    Y := PrinterMarginTop + VMap(22);
    Canvas.MoveTo(PrinterMarginLeftMirrored, Y);
    Canvas.LineTo(PageWidth - PrinterMarginRightMirrored, Y);
  end;
end;

{$IFDEF FPC}
initialization
  {$i main.lrs}
{$ELSE}
  {$R *.dfm}
{$ENDIF}
end.
