{
   Double Commander
   -------------------------------------------------------------------------
   Find dialog, with searching in thread

   Copyright (C) 2003-2004 Radek Cervinka (radek.cervinka@centrum.cz)
   Copyright (C) 2006-2015 Alexander Koblov (alexx2000@mail.ru)

   This program 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 program 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

}

unit fFindDlg;

{$mode objfpc}{$H+}
{$include calling.inc}

interface

uses
  Graphics, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls,
  ExtCtrls, Menus, EditBtn, Spin, Buttons, DateTimePicker, KASComboBox,
  fAttributesEdit, uDsxModule, DsxPlugin, uFindThread, uFindFiles,
  uSearchTemplate, fSearchPlugin, uFileView, types, DCStrUtils, ShellCtrls,
  ActnList, uOSForms, uShellContextMenu, uExceptions, uFileSystemFileSource,
  uFormCommands, uHotkeyManager;

const
  HotkeysCategory = 'Find files';


type

  { TfrmFindDlg }

  TfrmFindDlg = class(TForm,IFormCommands)
    actIntelliFocus: TAction;
    actCancel: TAction;
    actClose: TAction;
    actEdit: TAction;
    actGoToFile: TAction;
    actFeedToListbox: TAction;
    actCancelClose: TAction;
    actPageResults: TAction;
    actPageLoadSave: TAction;
    actPagePlugins: TAction;
    actPageAdvanced: TAction;
    actPageStandard: TAction;
    actView: TAction;
    actLastSearch: TAction;
    actNewSearch: TAction;
    actStart: TAction;
    actList: TActionList;
    Bevel2: TBevel;
    btnAddAttribute: TButton;
    btnAttrsHelp: TButton;
    btnClose: TButton;
    btnGoToPath: TButton;
    btnNewSearch: TButton;
    btnLastSearch: TButton;
    btnSaveTemplate: TButton;
    btnSearchDelete: TButton;
    btnSearchLoad: TButton;
    btnSearchSave: TButton;
    btnSearchSaveWithStartingPath: TButton;
    btnStart: TButton;
    btnUseTemplate: TButton;
    btnStop: TButton;
    btnView: TButton;
    btnEdit: TButton;
    btnWorkWithFound: TButton;
    cbFindText: TCheckBox;
    cbNotContainingText: TCheckBox;
    cbDateFrom: TCheckBox;
    cbNotOlderThan: TCheckBox;
    cbFileSizeFrom: TCheckBox;
    cbDateTo: TCheckBox;
    cbFileSizeTo: TCheckBox;
    cbReplaceText: TCheckBox;
    cbTimeFrom: TCheckBox;
    cbTimeTo: TCheckBox;
    cbPartialNameSearch: TCheckBox;
    cbFollowSymLinks: TCheckBox;
    cbUsePlugin: TCheckBox;
    cbSelectedFiles: TCheckBox;
    cbTextRegExp: TCheckBox;
    cbFindInArchive: TCheckBox;
    cbOpenedTabs: TCheckBox;
    cmbExcludeDirectories: TComboBoxWithDelItems;
    cmbNotOlderThanUnit: TComboBox;
    cmbFileSizeUnit: TComboBox;
    cmbEncoding: TComboBox;
    cmbSearchDepth: TComboBox;
    cbRegExp: TCheckBox;
    cmbPlugin: TComboBox;
    cmbReplaceText: TComboBoxWithDelItems;
    cmbFindText: TComboBoxWithDelItems;
    cmbExcludeFiles: TComboBoxWithDelItems;
    edtAttrib: TEdit;
    edtFindPathStart: TDirectoryEdit;
    frmContentPlugins: TfrmSearchPlugin;
    gbDirectories: TGroupBox;
    gbFiles: TGroupBox;
    lblAttributes: TLabel;
    lblExcludeDirectories: TLabel;
    lblCurrent: TLabel;
    lblExcludeFiles: TLabel;
    lblFound: TLabel;
    lblStatus: TLabel;
    lblTemplateHeader: TLabel;
    lbSearchTemplates: TListBox;
    lblSearchContents: TPanel;
    lblSearchDepth: TLabel;
    lblEncoding: TLabel;
    lsFoundedFiles: TListBox;
    CheksPanel: TPanel;
    miOpenInNewTab: TMenuItem;
    miShowInEditor: TMenuItem;
    miShowAllFound: TMenuItem;
    miRemoveFromLlist: TMenuItem;
    pnlDirectoriesDepth: TPanel;
    pnlLoadSaveBottomButtons: TPanel;
    pnlLoadSaveBottom: TPanel;
    pnlButtons: TPanel;
    pnlResultsBottomButtons: TPanel;
    pnlResults: TPanel;
    pnlStatus: TPanel;
    pnlResultsBottom: TPanel;
    seNotOlderThan: TSpinEdit;
    seFileSizeFrom: TSpinEdit;
    seFileSizeTo: TSpinEdit;
    pnlFindFile: TPanel;
    pgcSearch: TPageControl;
    tsPlugins: TTabSheet;
    tsResults: TTabSheet;
    tsLoadSave: TTabSheet;
    tsStandard: TTabSheet;
    lblFindPathStart: TLabel;
    lblFindFileMask: TLabel;
    cmbFindFileMask: TComboBoxWithDelItems;
    gbFindData: TGroupBox;
    cbCaseSens: TCheckBox;
    tsAdvanced: TTabSheet;
    PopupMenuFind: TPopupMenu;
    miShowInViewer: TMenuItem;
    ZVDateFrom: TDateTimePicker;
    ZVDateTo: TDateTimePicker;
    ZVTimeFrom: TDateTimePicker;
    ZVTimeTo: TDateTimePicker;

    procedure actExecute(Sender: TObject);
    procedure btnAddAttributeClick(Sender: TObject);
    procedure btnAttrsHelpClick(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
    procedure btnLastSearchClick(Sender: TObject);
    procedure btnNewSearchKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure btnSearchDeleteClick(Sender: TObject);
    procedure btnSearchLoadClick(Sender: TObject);
    procedure btnSearchSaveWithStartingPathClick(Sender: TObject);
    procedure btnSearchSaveClick(Sender: TObject);
    procedure cbCaseSensChange(Sender: TObject);
    procedure cbDateFromChange(Sender: TObject);
    procedure cbDateToChange(Sender: TObject);
    procedure cbFindInArchiveChange(Sender: TObject);
    procedure cbOpenedTabsChange(Sender: TObject);
    procedure cbPartialNameSearchChange(Sender: TObject);
    procedure cbRegExpChange(Sender: TObject);
    procedure cbTextRegExpChange(Sender: TObject);
    procedure cbSelectedFilesChange(Sender: TObject);
    procedure cmbEncodingSelect(Sender: TObject);
    procedure cbFindTextChange(Sender: TObject);
    procedure cbUsePluginChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnGoToPathClick(Sender: TObject);
    procedure btnNewSearchClick(Sender: TObject);
    procedure btnSelDirClick(Sender: TObject);
    procedure btnStartClick(Sender: TObject);
    procedure btnViewClick(Sender: TObject);
    procedure btnWorkWithFoundClick(Sender: TObject);
    procedure cbFileSizeFromChange(Sender: TObject);
    procedure cbFileSizeToChange(Sender: TObject);
    procedure cbNotOlderThanChange(Sender: TObject);
    procedure cbReplaceTextChange(Sender: TObject);
    procedure cbTimeFromChange(Sender: TObject);
    procedure cbTimeToChange(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure btnCloseClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure frmFindDlgClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
    procedure frmFindDlgShow(Sender: TObject);
    procedure gbDirectoriesResize(Sender: TObject);
    procedure lbSearchTemplatesDblClick(Sender: TObject);
    procedure lbSearchTemplatesSelectionChange(Sender: TObject; {%H-}User: boolean);
    procedure lsFoundedFilesDblClick(Sender: TObject);
    procedure lsFoundedFilesKeyDown(Sender: TObject;
      var Key: Word; Shift: TShiftState);
    procedure lsFoundedFilesMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure lsFoundedFilesMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure lsFoundedFilesMouseWheelDown(Sender: TObject; Shift: TShiftState;
      MousePos: TPoint; var Handled: Boolean);
    procedure lsFoundedFilesMouseWheelUp(Sender: TObject; Shift: TShiftState;
      MousePos: TPoint; var Handled: Boolean);
    procedure miOpenInNewTabClick(Sender: TObject);
    procedure miRemoveFromLlistClick(Sender: TObject);
    procedure miShowAllFoundClick(Sender: TObject);
    procedure miShowInEditorClick(Sender: TObject);
    procedure miShowInViewerClick(Sender: TObject);
    procedure pgcSearchChange(Sender: TObject);
    procedure seFileSizeFromChange(Sender: TObject);
    procedure seFileSizeToChange(Sender: TObject);
    procedure seNotOlderThanChange(Sender: TObject);
    procedure tsLoadSaveShow(Sender: TObject);
    procedure tsStandardEnter(Sender: TObject);
    procedure ZVDateFromChange(Sender: TObject);
    procedure ZVDateToChange(Sender: TObject);
    procedure ZVTimeFromChange(Sender: TObject);
    procedure ZVTimeToChange(Sender: TObject);
  private
    FSelectedFiles: TStringList;
    FFindThread:TFindThread;
    FTimeSearch:string;
    DsxPlugins: TDSXModuleList;
    FSearchingActive: Boolean;
    FFrmAttributesEdit: TfrmAttributesEdit;
    FLastTemplateName: String;
    FLastSearchTemplate: TSearchTemplate;
    FUpdateTimer: TTimer;
    FUpdating: Boolean;


    FRButtonPanelSender:TObject; // last focused button on Right Panel (pnlButtons)
    FCommands: TFormCommands;

    property Commands: TFormCommands read FCommands implements IFormCommands;

    procedure DisableControlsForTemplate;
    procedure StopSearch;
    procedure AfterSearchStopped;  //update button states after stop search(ThreadTerminate call this method)
    procedure AfterSearchFocus;     //set correct focus after search stopped

    procedure FillFindOptions(out FindOptions: TSearchTemplateRec; SetStartPath: Boolean);
    procedure FindOptionsToDSXSearchRec(const AFindOptions: TSearchTemplateRec;
                                        out SRec: TDsxSearchRecord);
    procedure FoundedStringCopyChanged(Sender: TObject);
    procedure LoadTemplate(const Template: TSearchTemplateRec);
    procedure LoadSelectedTemplate;
    procedure SaveTemplate(SaveStartingPath: Boolean);
    procedure SelectTemplate(const ATemplateName: String);
    procedure UpdateTemplatesList;
    procedure OnUpdateTimer(Sender: TObject);
    procedure OnAddAttribute(Sender: TObject);
    function InvalidRegExpr(AChecked: Boolean; const ARegExpr: String): Boolean;
  public
    class function Instance: TfrmFindDlg;
  public

    LastClickResultsPath:string;

    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure ClearFilter;
    procedure ClearResults;

    procedure ThreadTerminate(Sender:TObject);

    procedure FocusOnResults(Sender:TObject); // if press VK_LEFT or VK_RIGHT when on any button on left panel  - focus on results and remember button in FRButtonPanelSender

  published
    procedure cm_IntelliFocus(const Params: array of string);
    procedure cm_Start(const Params: array of string);
    procedure cm_CancelClose(const Params: array of string);
    procedure cm_Cancel(const Params: array of string);
    procedure cm_Close(const Params: array of string);
    procedure cm_NewSearch(const Params: array of string);
    procedure cm_LastSearch(const Params: array of string);
    procedure cm_View(const Params: array of string);
    procedure cm_Edit(const Params: array of string);
    procedure cm_GoToFile(const Params: array of string);
    procedure cm_FeedToListbox(const Params: array of string);

    procedure cm_PageStandard(const Params: array of string);
    procedure cm_PageAdvanced(const Params: array of string);
    procedure cm_PagePlugins(const Params: array of string);
    procedure cm_PageLoadSave(const Params: array of string);
    procedure cm_PageResults(const Params: array of string);


  end;

var
  FoundedStringCopy: TStringlist = nil;

{en
   Shows the find files dialog.
   Cannot store FileView reference as it might get destroyed while Find Dialog is running.
   We can store FileSource though, if needed in future (as it is reference counted).
   @param(FileView
          For which file view the find dialog is executed,
          to get file source, current path and a list of selected files.)
}
procedure ShowFindDlg(FileView: TFileView; const TemplateName: String);
function ShowDefineTemplateDlg(var TemplateName: String): Boolean;
function ShowUseTemplateDlg(var Template: TSearchTemplate): Boolean;

implementation

{$R *.lfm}

uses
  LCLProc, LCLType, LConvEncoding, StrUtils, HelpIntfs, fViewer, fMain,
  uLng, uGlobs, uShowForm, uDCUtils, uFileSource, uFileSourceUtil,
  uSearchResultFileSource, uFile,
  uFileViewNotebook, uKeyboard, uOSUtils, uArchiveFileSourceUtil,
  DCOSUtils, SynRegExpr;

const
  TimeUnitToComboIndex: array[TTimeUnit] of Integer = (0, 1, 2, 3, 4, 5, 6);
  ComboIndexToTimeUnit: array[0..6] of TTimeUnit = (tuSecond, tuMinute, tuHour, tuDay, tuWeek, tuMonth, tuYear);
  FileSizeUnitToComboIndex: array[TFileSizeUnit] of Integer = (0, 1, 2, 3, 4);
  ComboIndexToFileSizeUnit: array[0..4] of TFileSizeUnit = (suBytes, suKilo, suMega, suGiga, suTera);

type

  { TStringListTemp }

  TStringListTemp = class(TStringList)
  public
    function AddObject(const S: String; AObject: TObject): Integer; override;
  end;

var
  GfrmFindDlgInstance: TfrmFindDlg = nil;

procedure SAddFileProc({%H-}PlugNr: Integer; FoundFile: PChar); dcpcall;
var
  s: string;
begin
  s := string(FoundFile);
  if s='' then
    begin
      TfrmFindDlg.Instance.AfterSearchStopped;
      {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
        TfrmFindDlg.Instance.btnStart.Default := True;
      {$ENDIF}
    end
  else
    begin
     FoundedStringCopy.Add(s);
     Application.ProcessMessages;
    end;
end;

procedure SUpdateStatusProc({%H-}PlugNr: Integer; CurrentFile: PChar; FilesScanned: Integer); dcpcall;
var
  sCurrentFile: String;
begin
  sCurrentFile := String(CurrentFile);
  TfrmFindDlg.Instance.lblStatus.Caption:=Format(rsFindScanned,[FilesScanned])+TfrmFindDlg.Instance.FTimeSearch;
  if sCurrentFile = '' then
    TfrmFindDlg.Instance.lblCurrent.Caption := ''
  else
    TfrmFindDlg.Instance.lblCurrent.Caption:=rsFindScanning + ': ' + sCurrentFile;
  Application.ProcessMessages;
end;

procedure ShowFindDlg(FileView: TFileView; const TemplateName: String);
var
  ASelectedFiles: TFiles = nil;
  I: Integer;
begin
  if not Assigned(FileView) then
    raise Exception.Create('ShowFindDlg: FileView=nil');

  with TfrmFindDlg.Instance do
  begin
    // Prepare window for search files
    ClearFilter;
    Caption := rsFindSearchFiles;
    edtFindPathStart.Text := FileView.CurrentPath;

    // Get paths of selected files, if any.
    FSelectedFiles.Clear;
    ASelectedFiles := FileView.CloneSelectedFiles;
    if Assigned(ASelectedFiles) then
    try
      if ASelectedFiles.Count > 0 then
      begin
        for I := 0 to ASelectedFiles.Count - 1 do
          FSelectedFiles.Add(ASelectedFiles[I].FullPath);
      end;
    finally
      FreeAndNil(ASelectedFiles);
    end;

    if Length(TemplateName) > 0 then
    begin
      FUpdating:= True;
      UpdateTemplatesList;
      SelectTemplate(TemplateName);
      LoadSelectedTemplate;
      FUpdating:= False;
    end;

    ShowOnTop;
  end;
end;

function ShowDefineTemplateDlg(var TemplateName: String): Boolean;
var
  AIndex: Integer;
  AForm: TfrmFindDlg;
begin
  AForm := TfrmFindDlg.Create(nil);
  try
    with AForm do
    begin
      // Prepare window for define search template
      Caption := rsFindDefineTemplate;
      AForm.DisableControlsForTemplate;
      btnSaveTemplate.Visible := True;
      btnSaveTemplate.Default := True;
      BorderIcons := [biSystemMenu, biMaximize];
      if Length(TemplateName) > 0 then
      begin
        UpdateTemplatesList;
        AIndex:= lbSearchTemplates.Items.IndexOf(TemplateName);
        if AIndex >= 0 then
        begin
          lbSearchTemplates.ItemIndex:= AIndex;
          AForm.LoadSelectedTemplate;
        end;
      end;
      Result:= (ShowModal = mrOK);
      if Result and (lbSearchTemplates.Count > 0) then
      begin
        TemplateName:= lbSearchTemplates.Items[lbSearchTemplates.Count - 1];
      end;
    end;
  finally
    AForm.Free;
  end;
end;

function ShowUseTemplateDlg(var Template: TSearchTemplate): Boolean;
var
  AForm: TfrmFindDlg;
  SearchRec: TSearchTemplateRec;
begin
  AForm := TfrmFindDlg.Create(nil);
  try
    with AForm do
    begin
      // Prepare window for define search template
      Caption := rsFindDefineTemplate;
      DisableControlsForTemplate;
      btnUseTemplate.Visible := True;
      btnUseTemplate.Default := True;
      BorderIcons := [biSystemMenu, biMaximize];
      if Assigned(Template) then
        AForm.LoadTemplate(Template.SearchRecord);
      Result:= (ShowModal = mrOK);
      if Result then
      begin
        if not Assigned(Template) then
          Template:= TSearchTemplate.Create;
        try
          Template.TemplateName := AForm.FLastTemplateName;
          AForm.FillFindOptions(SearchRec, False);
          Template.SearchRecord := SearchRec;
        except
          FreeAndNil(Template);
          raise;
        end;
      end;
    end;
  finally
    AForm.Free;
  end;
end;

{ TStringListTemp }

function TStringListTemp.AddObject(const S: String; AObject: TObject): Integer;
begin
  Result:= Count;
  InsertItem(Result, S, AObject);
end;

{ TfrmFindDlg }

procedure TfrmFindDlg.FormCreate(Sender: TObject);
var
  I: Integer;
  HMFindFiles: THMForm;
begin
  Height:= pnlFindFile.Height + 22;
  DsxPlugins := TDSXModuleList.Create;
  DsxPlugins.Assign(gDSXPlugins);
  FoundedStringCopy := TStringListTemp.Create;
  FoundedStringCopy.OnChange:=@FoundedStringCopyChanged;

  // load language
  edtFindPathStart.DialogTitle:= rsFindWhereBeg;
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitSecond);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitMinute);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitHour);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitDay);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitWeek);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitMonth);
  cmbNotOlderThanUnit.Items.Add(rsTimeUnitYear);
  cmbFileSizeUnit.Items.Add(rsSizeUnitBytes);
  cmbFileSizeUnit.Items.Add(rsSizeUnitKBytes);
  cmbFileSizeUnit.Items.Add(rsSizeUnitMBytes);
  cmbFileSizeUnit.Items.Add(rsSizeUnitGBytes);
  cmbFileSizeUnit.Items.Add(rsSizeUnitTBytes);

  // fill search depth combobox
  cmbSearchDepth.Items.Add(rsFindDepthAll);
  cmbSearchDepth.Items.Add(rsFindDepthCurDir);
  for I:= 1 to 100 do
    cmbSearchDepth.Items.Add(Format(rsFindDepth, [IntToStr(I)]));
  cmbSearchDepth.ItemIndex:= 0;
  // fill encoding combobox
  cmbEncoding.Clear;
  GetSupportedEncodings(cmbEncoding.Items);
  cmbEncoding.ItemIndex:= cmbEncoding.Items.IndexOf(EncodingAnsi);

  // gray disabled fields
  cbUsePluginChange(Sender);
  cbFindTextChange(Sender);
  cbReplaceTextChange(Sender);
  cbNotOlderThanChange(Sender);
  cbFileSizeFromChange(Sender);
  cbFileSizeToChange(Sender);
  ZVDateFrom.DateTime:=Now();
  ZVDateTo.DateTime:=Now();
  ZVTimeFrom.DateTime:=Now();
  ZVTimeTo.DateTime:=Now();
  cbDateFrom.Checked:=False;
  cbDateTo.Checked:=False;
  cbTimeFrom.Checked:=False;
  cbTimeTo.Checked:=False;

{$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
  btnStart.Default := True;
{$ENDIF}

  cmbNotOlderThanUnit.ItemIndex := 3; // Days
  cmbFileSizeUnit.ItemIndex := 1; // Kilobytes
  edtFindPathStart.ShowHidden := gShowSystemFiles;
  cbPartialNameSearch.Checked:= gPartialNameSearch;

  InitPropStorage(Self);

  HMFindFiles := HotMan.Register(Self, HotkeysCategory);
  HMFindFiles.RegisterActionList(actList);
end;

procedure TfrmFindDlg.cbUsePluginChange(Sender: TObject);
begin
  EnableControl(cmbPlugin, cbUsePlugin.Checked);

  if not FUpdating and cmbPlugin.Enabled and cmbPlugin.CanFocus and (Sender = cbUsePlugin) then
  begin
    cmbPlugin.SetFocus;
    cmbPlugin.SelectAll;
  end;
end;

procedure TfrmFindDlg.cmbEncodingSelect(Sender: TObject);
begin
  if not gUseMmapInSearch then
  begin
    if cmbEncoding.ItemIndex <> cmbEncoding.Items.IndexOf(EncodingAnsi) then
      begin
        cbCaseSens.Tag:= Integer(cbCaseSens.Checked);
        cbCaseSens.Checked:= True;
        cbCaseSens.Enabled:= False;
      end
    else
      begin
        cbCaseSens.Checked:= Boolean(cbCaseSens.Tag);
        cbCaseSens.Enabled:= True;
      end;
  end;
end;

constructor TfrmFindDlg.Create(TheOwner: TComponent);
begin
  FSelectedFiles := TStringList.Create;
  inherited Create(TheOwner);
  FUpdateTimer := TTimer.Create(Self);
  FUpdateTimer.Interval := 100;
  FUpdateTimer.Enabled := False;
  FUpdateTimer.OnTimer := @OnUpdateTimer;

  FCommands := TFormCommands.Create(Self, actList);
end;

destructor TfrmFindDlg.Destroy;
begin
  inherited Destroy;
  FSelectedFiles.Free;
  FLastSearchTemplate.Free;
end;

procedure TfrmFindDlg.DisableControlsForTemplate;
begin
  lblFindPathStart.Visible := False;
  edtFindPathStart.Visible := False;
  cbFollowSymLinks.Visible := False;
  cbSelectedFiles.Visible := False;
  btnStart.Visible := False;
  btnStop.Visible := False;
  btnNewSearch.Visible := False;
  btnLastSearch.Visible := False;
  btnSearchSaveWithStartingPath.Visible := False;
  gbFindData.Visible := False;
  tsPlugins.TabVisible := False;
  tsResults.TabVisible := False;
end;

procedure TfrmFindDlg.cbFindTextChange(Sender: TObject);
begin
  EnableControl(cmbFindText, cbFindText.Checked);
  EnableControl(cmbEncoding, cbFindText.Checked);
  EnableControl(cbCaseSens, cbFindText.Checked);
  EnableControl(cbReplaceText, cbFindText.Checked and not cbFindInArchive.Checked);
  EnableControl(cbNotContainingText, cbFindText.Checked);
  EnableControl(cbTextRegExp, cbFindText.Checked);
  lblEncoding.Enabled:=cbFindText.Checked;
  cbReplaceText.Checked:= False;

  if not FUpdating and cmbFindText.Enabled and cmbFindText.CanFocus and (Sender = cbFindText) then
  begin
    cmbFindText.SetFocus;
    cmbFindText.SelectAll;
  end;
end;

procedure TfrmFindDlg.ClearFilter;
begin
  FUpdating := True;

  FLastTemplateName := '';
  edtFindPathStart.Text:= '';
  edtFindPathStart.ShowHidden := gShowSystemFiles;
  cmbExcludeDirectories.Text := '';
  cmbSearchDepth.ItemIndex := 0;
  cmbFindFileMask.Text:= '*';
  cmbExcludeFiles.Text := '';
  cbPartialNameSearch.Checked:= gPartialNameSearch;
  cbRegExp.Checked := False;

  // attributes
  edtAttrib.Text:= '';

  // file date/time
  ZVDateFrom.DateTime:=Now();
  ZVDateTo.DateTime:=Now();
  ZVTimeFrom.DateTime:=Now();
  ZVTimeTo.DateTime:=Now();
  cbDateFrom.Checked:=False;
  cbDateTo.Checked:=False;
  cbTimeFrom.Checked:=False;
  cbTimeTo.Checked:=False;

  // not older then
  cbNotOlderThan.Checked:= False;
  seNotOlderThan.Value:= 1;
  cmbNotOlderThanUnit.ItemIndex := 3; // Days

  // file size
  cbFileSizeFrom.Checked:= False;
  cbFileSizeTo.Checked:= False;
  seFileSizeFrom.Value:= 0;
  seFileSizeTo.Value:= 10;
  cmbFileSizeUnit.ItemIndex := 1; // Kilobytes

  // find/replace text
  // do not clear search/replace text just clear checkbox
  cbFindText.Checked:= False;
  cbReplaceText.Checked:= False;
  cbCaseSens.Checked:= False;
  cbNotContainingText.Checked:= False;
  cmbEncoding.ItemIndex := 0;

  // plugins
  cmbPlugin.Text:= '';

  FUpdating := False;
end;

procedure TfrmFindDlg.ClearResults;
begin
  lsFoundedFiles.Clear;
  lsFoundedFiles.Tag:= 0;
  lsFoundedFiles.ScrollWidth:= 0;
  FoundedStringCopy.Clear;
end;

procedure TfrmFindDlg.btnSearchLoadClick(Sender: TObject);
begin
  LoadSelectedTemplate;
end;

procedure TfrmFindDlg.btnSearchSaveWithStartingPathClick(Sender: TObject);
begin
  SaveTemplate(True);
end;

procedure TfrmFindDlg.btnSearchDeleteClick(Sender: TObject);
var
  OldIndex: Integer;
begin
  OldIndex := lbSearchTemplates.ItemIndex;
  if OldIndex < 0 then Exit;
  gSearchTemplateList.DeleteTemplate(OldIndex);
  lbSearchTemplates.Items.Delete(OldIndex);
  if OldIndex < lbSearchTemplates.Count then
    lbSearchTemplates.ItemIndex := OldIndex
  else if lbSearchTemplates.Count > 0 then
    lbSearchTemplates.ItemIndex := lbSearchTemplates.Count - 1;
end;

procedure TfrmFindDlg.btnAttrsHelpClick(Sender: TObject);
begin
  ShowHelpOrErrorForKeyword('', edtAttrib.HelpKeyword);
end;

procedure TfrmFindDlg.btnEditClick(Sender: TObject);
begin
  if lsFoundedFiles.ItemIndex <> -1 then
    ShowEditorByGlob(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]);
end;

procedure TfrmFindDlg.actExecute(Sender: TObject);
var
  cmd: string;
begin
  cmd := (Sender as TAction).Name;
  cmd := 'cm_' + Copy(cmd, 4, Length(cmd) - 3);
  Commands.ExecuteCommand(cmd, []);
end;


procedure TfrmFindDlg.btnAddAttributeClick(Sender: TObject);
begin
  if not Assigned(FFrmAttributesEdit) then
  begin
    FFrmAttributesEdit := TfrmAttributesEdit.Create(Self);
    FFrmAttributesEdit.OnOk := @OnAddAttribute;
  end;
  FFrmAttributesEdit.Reset;
  if not (fsModal in FormState) then
    FFrmAttributesEdit.Show
  else begin
    FFrmAttributesEdit.ShowModal;
  end;
end;

procedure TfrmFindDlg.btnSearchSaveClick(Sender: TObject);
begin
  SaveTemplate(False);
end;

procedure TfrmFindDlg.cbCaseSensChange(Sender: TObject);
begin
  if cbCaseSens.Checked then cbTextRegExp.Checked:= False;
end;

procedure TfrmFindDlg.cbDateFromChange(Sender: TObject);
begin
  UpdateColor(ZVDateFrom, cbDateFrom.Checked);
end;

procedure TfrmFindDlg.cbDateToChange(Sender: TObject);
begin
  UpdateColor(ZVDateTo, cbDateTo.Checked);
end;

procedure TfrmFindDlg.cbFindInArchiveChange(Sender: TObject);
begin
  EnableControl(cbReplaceText, cbFindText.Checked and not cbFindInArchive.Checked);
  if cbReplaceText.Checked then cbReplaceText.Checked := cbReplaceText.Enabled;
  btnView.Enabled:= not cbFindInArchive.Checked;
  btnEdit.Enabled:= not cbFindInArchive.Checked;
  btnWorkWithFound.Enabled:= not cbFindInArchive.Checked;
  cbReplaceTextChange(cbReplaceText);
end;

procedure TfrmFindDlg.cbOpenedTabsChange(Sender: TObject);
begin
  cbSelectedFiles.Enabled:=not cbOpenedTabs.Checked;
  cbFollowSymLinks.Enabled:=not cbOpenedTabs.Checked;
  edtFindPathStart.Enabled:=not cbOpenedTabs.Checked;
end;

procedure TfrmFindDlg.cbPartialNameSearchChange(Sender: TObject);
begin
  if cbPartialNameSearch.Checked then cbRegExp.Checked:=False;
end;

procedure TfrmFindDlg.cbRegExpChange(Sender: TObject);
begin
  if cbRegExp.Checked then cbPartialNameSearch.Checked:=False;
end;

procedure TfrmFindDlg.cbTextRegExpChange(Sender: TObject);
begin
  if cbTextRegExp.Checked then cbCaseSens.Checked:= False;
end;

procedure TfrmFindDlg.cbSelectedFilesChange(Sender: TObject);
begin
  edtFindPathStart.Enabled := not cbSelectedFiles.Checked;
end;

procedure TfrmFindDlg.btnSelDirClick(Sender: TObject);
var
  s:String;
begin
  s:=edtFindPathStart.Text;
  if not mbDirectoryExists(s) then s:='';
  SelectDirectory(rsFindWhereBeg,'',s, False);
  edtFindPathStart.Text:=s;
end;

procedure TfrmFindDlg.btnNewSearchClick(Sender: TObject);
begin
  StopSearch;
  pgcSearch.PageIndex:= 0;
  ClearResults;
  miShowAllFound.Enabled:=False;
  lblStatus.Caption:= EmptyStr;
  lblCurrent.Caption:= EmptyStr;
  lblFound.Caption:= EmptyStr;
  if pgcSearch.ActivePage = tsStandard then
    cmbFindFileMask.SetFocus;

//  AfterSearchStopped;

  {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
    btnStart.Default := True;
  {$ENDIF}
end;

procedure TfrmFindDlg.btnGoToPathClick(Sender: TObject);
var
  AFile: TFile = nil;
  TargetFile: String;
  ArchiveFile: String;
  FileSource: IFileSource;
begin
  if lsFoundedFiles.ItemIndex <> -1 then
  try
    StopSearch;
    if (lsFoundedFiles.Items.Objects[lsFoundedFiles.ItemIndex] <> nil) then
    begin
      TargetFile:= lsFoundedFiles.Items[lsFoundedFiles.ItemIndex];
      ArchiveFile:= ExtractWord(1, TargetFile, [ReversePathDelim]);
      TargetFile:= PathDelim + ExtractWord(2, TargetFile, [ReversePathDelim]);
      AFile:= TFileSystemFileSource.CreateFileFromFile(ArchiveFile);
      try
        FileSource:= GetArchiveFileSource(TFileSystemFileSource.GetFileSource, AFile);
      finally
        AFile.Free;
      end;
      if Assigned(FileSource) then
      begin
        frmMain.ActiveFrame.AddFileSource(FileSource, ExtractFilePath(TargetFile));
        frmMain.ActiveFrame.SetActiveFile(ExtractFileName(TargetFile));
      end;
    end
    else begin
      SetFileSystemPath(frmMain.ActiveFrame, ExtractFilePath(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]));
      frmMain.ActiveFrame.SetActiveFile(ExtractFileName(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]));
    end;
    Close;
  except
    on E: Exception do MessageDlg(E.Message, mtError, [mbOK], 0);
  end;
end;

procedure TfrmFindDlg.btnLastSearchClick(Sender: TObject);
begin
  if Assigned(FLastSearchTemplate) then
  begin
    LoadTemplate(FLastSearchTemplate.SearchRecord);
    pgcSearch.ActivePage := tsStandard;
    cmbFindFileMask.SetFocus;
  end;
end;

procedure TfrmFindDlg.btnNewSearchKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if ((Key=VK_LEFT)or(Key=VK_RIGHT))and(lsFoundedFiles.Count>0) then FocusOnResults(Sender);
end;

procedure TfrmFindDlg.FillFindOptions(out FindOptions: TSearchTemplateRec; SetStartPath: Boolean);
begin
  with FindOptions do
  begin
    if SetStartPath then
      StartPath := edtFindPathStart.Text
    else
      StartPath := '';
    ExcludeDirectories  := cmbExcludeDirectories.Text;
    FilesMasks          := cmbFindFileMask.Text;
    ExcludeFiles        := cmbExcludeFiles.Text;
    SearchDepth         := cmbSearchDepth.ItemIndex - 1;
    RegExp              := cbRegExp.Checked;
    IsPartialNameSearch := cbPartialNameSearch.Checked;
    FollowSymLinks      := cbFollowSymLinks.Checked;
    FindInArchives      := cbFindInArchive.Checked;

    { File attributes }
    AttributesPattern   := edtAttrib.Text;

    { Date/time }
    DateTimeFrom := 0;
    DateTimeTo   := 0;
    IsDateFrom   := False;
    IsDateTo     := False;
    IsTimeFrom   := False;
    IsTimeTo     := False;
    if cbDateFrom.Checked then
      begin
        IsDateFrom := True;
        DateTimeFrom := ZVDateFrom.Date;
      end;
    if cbDateTo.Checked then
      begin
        IsDateTo := True;
        DateTimeTo := ZVDateTo.Date;
      end;
    if cbTimeFrom.Checked then
      begin
        IsTimeFrom := True;
        DateTimeFrom := DateTimeFrom + ZVTimeFrom.Time;
      end;
    if cbTimeTo.Checked then
      begin
        IsTimeTo := True;
        DateTimeTo := DateTimeTo + ZVTimeTo.Time;
      end;

    { Not Older Than }
    IsNotOlderThan   := cbNotOlderThan.Checked;
    NotOlderThan     := seNotOlderThan.Value;
    NotOlderThanUnit := ComboIndexToTimeUnit[cmbNotOlderThanUnit.ItemIndex];

    { File size }
    IsFileSizeFrom := cbFileSizeFrom.Checked;
    IsFileSizeTo   := cbFileSizeTo.Checked;
    FileSizeFrom   := seFileSizeFrom.Value;
    FileSizeTo     := seFileSizeTo.Value;
    FileSizeUnit   := ComboIndexToFileSizeUnit[cmbFileSizeUnit.ItemIndex];

    { Find/replace text }
    IsFindText        := cbFindText.Checked;
    FindText          := cmbFindText.Text;
    IsReplaceText     := cbReplaceText.Checked;
    ReplaceText       := cmbReplaceText.Text;
    CaseSensitive     := cbCaseSens.Checked;
    NotContainingText := cbNotContainingText.Checked;
    TextRegExp        := cbTextRegExp.Checked;
    TextEncoding      := cmbEncoding.Text;
    { Plugins }
    SearchPlugin      := cmbPlugin.Text;
    frmContentPlugins.Save(FindOptions)
  end;
end;

procedure TfrmFindDlg.FindOptionsToDSXSearchRec(
  const AFindOptions: TSearchTemplateRec;
  out SRec: TDsxSearchRecord);
begin
  with AFindOptions do
  begin
    FillByte(SRec{%H-}, SizeOf(SRec), 0);

    SRec.StartPath:= Copy(StartPath, 1, SizeOf(SRec.StartPath));

    if IsPartialNameSearch then
      SRec.FileMask:= '*' + Copy(FilesMasks, 1, SizeOf(SRec.FileMask) - 2) + '*'
    else
      SRec.FileMask:= Copy(FilesMasks, 1, SizeOf(SRec.FileMask));

    SRec.Attributes:= faAnyFile;  // AttrStrToFileAttr?
    SRec.AttribStr:= Copy(AttributesPattern, 1, SizeOf(SRec.AttribStr));

    SRec.CaseSensitive:=CaseSensitive;
    {Date search}
    SRec.IsDateFrom:=IsDateFrom;
    SRec.IsDateTo:=IsDateTo;
    SRec.DateTimeFrom:=DateTimeFrom;
    SRec.DateTimeTo:=DateTimeTo;
    {Time search}
    SRec.IsTimeFrom:=IsTimeFrom;
    SRec.IsTimeTo:=IsTimeTo;
    (* File size search *)
    SRec.IsFileSizeFrom:=IsFileSizeFrom;
    SRec.IsFileSizeTo:=IsFileSizeTo;
    SRec.FileSizeFrom:=FileSizeFrom;
    SRec.FileSizeTo:=FileSizeTo;
    (* Find text *)
    SRec.NotContainingText:=NotContainingText;
    SRec.IsFindText:=IsFindText;
    SRec.FindText:= Copy(FindText, 1, SizeOf(SRec.FindText));
    (* Replace text *)
    SRec.IsReplaceText:=IsReplaceText;
    SRec.ReplaceText:= Copy(ReplaceText, 1, SizeOf(SRec.ReplaceText));
  end;
end;

procedure TfrmFindDlg.StopSearch;
begin
  if FSearchingActive then
  begin
    if (cbUsePlugin.Checked) and (cmbPlugin.ItemIndex<>-1) then
    begin
      DSXPlugins.GetDSXModule(cmbPlugin.ItemIndex).CallStopSearch;
      DSXPlugins.GetDSXModule(cmbPlugin.ItemIndex).CallFinalize;
      AfterSearchStopped;
      AfterSearchFocus;
//      btnNewSearch.SetFocus;
    end;

    if Assigned(FFindThread) then
    begin
      FFindThread.Terminate;
      FFindThread := nil;
    end;
  end;
end;

class function TfrmFindDlg.Instance: TfrmFindDlg;
begin
  if not Assigned(GfrmFindDlgInstance) then
    GfrmFindDlgInstance := TfrmFindDlg.Create(nil);
  Result := GfrmFindDlgInstance;
end;

procedure TfrmFindDlg.lbSearchTemplatesDblClick(Sender: TObject);
begin
  LoadSelectedTemplate;
end;

procedure TfrmFindDlg.AfterSearchStopped;
begin
  btnStop.Enabled:= False;
  btnStart.Enabled:= True;
  btnClose.Enabled:= True;
  btnNewSearch.Enabled:= True;
  FSearchingActive := False;

//  btnNewSearch.SetFocus;
end;

procedure TfrmFindDlg.AfterSearchFocus;
var
  LastButton: TButton;
begin
  if Assigned(Self) and Visible then
  begin
    if FRButtonPanelSender <> nil then  // if user press a keys while search - keep focus on it
    begin
        LastButton:= (FRButtonPanelSender as TButton);
        if LastButton.Enabled then LastButton.SetFocus else btnNewSearch.SetFocus;
    end
    else begin                          // if user don't press anything - focus on results
      if (lsFoundedFiles.Count > 0) then
      begin
        lsFoundedFiles.SetFocus;
        if (lsFoundedFiles.ItemIndex <> -1) then
          lsFoundedFiles.Selected[lsFoundedFiles.ItemIndex]:= True;
      end else
      begin
        if btnNewSearch.Enabled then btnNewSearch.SetFocus else btnStart.SetFocus;
      end;
    end;
  end;
end;

procedure TfrmFindDlg.btnStartClick(Sender: TObject);
var
  sTemp, sPath : String;
  sr: TDsxSearchRecord;
  SearchTemplate, TmpTemplate: TSearchTemplateRec;
  PassedSelectedFiles: TStringList = nil;
begin
  sTemp:= edtFindPathStart.Text;
  repeat
    sPath:= Copy2SymbDel(sTemp, ';');
    if not mbDirectoryExists(sPath) then
      begin
        ShowMessage(Format(rsFindDirNoEx,[sPath]));
        Exit;
      end;
  until sTemp = EmptyStr;
  // add to find mask history
  InsertFirstItem(cmbFindFileMask.Text, cmbFindFileMask);
  // add to exclude directories history
  InsertFirstItem(cmbExcludeDirectories.Text, cmbExcludeDirectories);
  // add to exclude files history
  InsertFirstItem(cmbExcludeFiles.Text, cmbExcludeFiles);
  // add to search text history
  if cbFindText.Checked then
  begin
    InsertFirstItem(cmbFindText.Text, cmbFindText);
    // update search history, so it can be used in
    // Viewer/Editor opened from find files dialog
    gFirstTextSearch:= False;
    glsSearchHistory.Assign(cmbFindText.Items);
  end;
  // add to replace text history
  if cbReplaceText.Checked then
  begin
    InsertFirstItem(cmbReplaceText.Text, cmbReplaceText);
    // update replace history, so it can be used in
    // Editor opened from find files dialog (issue 0000539)
    glsReplaceHistory.Assign(cmbReplaceText.Items);
  end;

  if cbSelectedFiles.Checked and (FSelectedFiles.Count = 0) then
  begin
    ShowMessage(rsMsgNoFilesSelected);
    cbSelectedFiles.Checked:= False;
    Exit;
  end;

  // Show search results page
  pgcSearch.ActivePageIndex:= pgcSearch.PageCount - 1;

  if lsFoundedFiles.CanFocus then
    lsFoundedFiles.SetFocus;

  ClearResults;
  miShowAllFound.Enabled:=False;

  FSearchingActive := True;
  btnStop.Enabled:=True;
{$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
  btnStop.Default:=True;
{$ENDIF}
  btnStart.Enabled:= False;
  btnClose.Enabled:= False;
  btnNewSearch.Enabled:= False;

  FillFindOptions(SearchTemplate, True);

  if not Assigned(FLastSearchTemplate) then
    FLastSearchTemplate := TSearchTemplate.Create;
  TmpTemplate := SearchTemplate;
  TmpTemplate.StartPath := ''; // Don't remember starting path.
  FLastSearchTemplate.SearchRecord := TmpTemplate;

  try
    if (cbUsePlugin.Checked) and (cmbPlugin.ItemIndex<>-1) then
      begin
        if DSXPlugins.LoadModule(cmbPlugin.ItemIndex) then
        begin
          FindOptionsToDSXSearchRec(SearchTemplate, sr);
          DSXPlugins.GetDSXModule(cmbPlugin.ItemIndex).CallInit(@SAddFileProc,@SUpdateStatusProc);
          DSXPlugins.GetDSXModule(cmbPlugin.ItemIndex).CallStartSearch(sr);
        end
        else
          StopSearch;
      end
    else
      begin
        if cbSelectedFiles.Checked then PassedSelectedFiles := FSelectedFiles;

        if cbOpenedTabs.Checked then
        begin
          frmMain.GetListOpenedPaths(FSelectedFiles);
          PassedSelectedFiles := FSelectedFiles;
        end;

        FFindThread := TFindThread.Create(SearchTemplate, PassedSelectedFiles);
        with FFindThread do
        begin
          Items := FoundedStringCopy;
          OnTerminate := @ThreadTerminate; // will update the buttons after search is finished
        end;

        FTimeSearch:='';
        FFindThread.Start;
        FUpdateTimer.Enabled := True;
        FUpdateTimer.OnTimer(FUpdateTimer);

        FRButtonPanelSender:=nil;
      end;
  except
    StopSearch;
    raise;
  end;
end;

procedure TfrmFindDlg.FoundedStringCopyChanged(Sender: TObject);
var
  sText: String;
  iTemp: Integer;
begin
  if FoundedStringCopy.Count > 0 then
  begin
    iTemp:= FoundedStringCopy.Count - 1;
    Sender:= FoundedStringCopy.Objects[iTemp];
    sText:= FoundedStringCopy[iTemp];
    iTemp:= Length(sText);
    if iTemp > lsFoundedFiles.Tag then
    begin
      lsFoundedFiles.Tag:= iTemp;
      iTemp:= lsFoundedFiles.Canvas.TextWidth(sText);
      if iTemp > lsFoundedFiles.ScrollWidth then
        lsFoundedFiles.ScrollWidth:= iTemp + 32;
    end;
    lsFoundedFiles.Items.AddObject(sText, Sender);
  end;
end;

procedure TfrmFindDlg.btnViewClick(Sender: TObject);
begin
  if lsFoundedFiles.ItemIndex <> -1 then
    ShowViewerByGlob(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]);
end;

procedure TfrmFindDlg.btnWorkWithFoundClick(Sender: TObject);
var
  I: Integer;
  sFileName: String;
  SearchResultFS: ISearchResultFileSource;
  FileList: TFileTree;
  aFile: TFile;
  Notebook: TFileViewNotebook;
  NewPage: TFileViewPage;
begin
  StopSearch;

  FileList := TFileTree.Create;
  for i := 0 to lsFoundedFiles.Items.Count - 1 do
  begin
    sFileName:= lsFoundedFiles.Items[I];
    try
      aFile := TFileSystemFileSource.CreateFileFromFile(sFileName);
      FileList.AddSubNode(aFile);
    except
      on EFileNotFound do;
    end;
  end;

  // Create search result file source.
  // Currently only searching FileSystem is supported.
  SearchResultFS := TSearchResultFileSource.Create;
  SearchResultFS.AddList(FileList, TFileSystemFileSource.GetFileSource);

  // Add new tab for search results.
  Notebook := frmMain.ActiveNotebook;
  NewPage := Notebook.NewPage(Notebook.ActiveView);
  NewPage.FileView.AddFileSource(SearchResultFS, SearchResultFS.GetRootDir);
  NewPage.FileView.FlatView := True;
  NewPage.MakeActive;

  Close;
end;

procedure TfrmFindDlg.cbFileSizeFromChange(Sender: TObject);
begin
  UpdateColor(seFileSizeFrom, cbFileSizeFrom.Checked);
  EnableControl(cmbFileSizeUnit,cbFileSizeFrom.Checked or cbFileSizeTo.Checked);
end;

procedure TfrmFindDlg.cbFileSizeToChange(Sender: TObject);
begin
  UpdateColor(seFileSizeTo, cbFileSizeTo.Checked);
  EnableControl(cmbFileSizeUnit,cbFileSizeFrom.Checked or cbFileSizeTo.Checked);
end;

procedure TfrmFindDlg.cbNotOlderThanChange(Sender: TObject);
begin
   UpdateColor(seNotOlderThan, cbNotOlderThan.Checked);
   EnableControl(cmbNotOlderThanUnit,cbNotOlderThan.Checked);
end;

procedure TfrmFindDlg.cbReplaceTextChange(Sender: TObject);
begin
  EnableControl(cmbReplaceText, cbReplaceText.Checked and cbFindText.Checked);
  cbNotContainingText.Checked := False;
  cbNotContainingText.Enabled := (not cbReplaceText.Checked and cbFindText.Checked);

  if not FUpdating and cmbReplaceText.Enabled and cmbReplaceText.CanFocus then
  begin
    cmbReplaceText.SetFocus;
    cmbReplaceText.SelectAll;
  end;
end;

procedure TfrmFindDlg.cbTimeFromChange(Sender: TObject);
begin
  UpdateColor(ZVTimeFrom, cbTimeFrom.Checked);
end;

procedure TfrmFindDlg.cbTimeToChange(Sender: TObject);
begin
  UpdateColor(ZVTimeTo, cbTimeTo.Checked);
end;

procedure TfrmFindDlg.ThreadTerminate(Sender:TObject);
begin
  FFindThread := TFindThread(Sender);
  if FFindThread.TimeOfScan<>0 then FTimeSearch:=' , '+rsFindTimeOfScan+formatdatetime('hh:nn:ss.zzz', FFindThread.TimeOfScan);
  FUpdateTimer.OnTimer(FUpdateTimer);
  FUpdateTimer.Enabled := False;
  FFindThread := nil;
  AfterSearchStopped;
  AfterSearchFocus;

  {
  if Assigned(Self) and Visible then
  begin
    if FRButtonPanelSender<>nil then  // if user press a keys while search - keep focus on it
    begin
        LastButton:=(FRButtonPanelSender as TButton);
        if LastButton.Enabled then LastButton.SetFocus else btnNewSearch.SetFocus;
    end else
    begin// if user don't press anything - focus on results
        lsFoundedFiles.SetFocus;
        if lsFoundedFiles.Count>0 then lsFoundedFiles.Selected[lsFoundedFiles.ItemIndex]:=True;
    end;
  end;
  }

//  if Assigned(Self) and Visible then btnNewSearch.SetFocus;
//  if Self.Instance;
end;

procedure TfrmFindDlg.FocusOnResults(Sender: TObject);
begin
  FRButtonPanelSender:=Sender;

  if pgcSearch.ActivePage=tsResults then
  begin
    {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
      btnStart.Default:= False;
    {$ENDIF}
    if lsFoundedFiles.SelCount=0 then lsFoundedFiles.ItemIndex:=0;
    lsFoundedFiles.SetFocus;
    lsFoundedFiles.Selected[lsFoundedFiles.ItemIndex]:=True;

  end;
end;

procedure TfrmFindDlg.cm_IntelliFocus(const Params: array of string);
begin
    if FFindThread<>nil then
    begin
      FFindThread.OnTerminate:=nil;
      FFindThread.Terminate;
      FUpdateTimer.OnTimer(FUpdateTimer);
      FUpdateTimer.Enabled := False;
      FFindThread := nil;
    end;

    AfterSearchStopped;
    {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
      btnStart.Default := True;
    {$ENDIF}

    if cmbFindText.Focused then // if F7 on already focused textSearch field- disable text search and set focun on file mask
    begin
      cbFindText.Checked:=False;
      cmbFindFileMask.SetFocus;
      cmbFindFileMask.SelectAll;
      exit;
    end else
    begin
      pgcSearch.PageIndex:= 0;
      cbFindText.Checked:=True;
      cmbFindText.SetFocus;
      cmbFindText.SelectAll;
    end;
end;

procedure TfrmFindDlg.cm_Start(const Params: array of string);
begin
  btnStart.Click;
end;

procedure TfrmFindDlg.cm_CancelClose(const Params: array of string);
begin
  if FSearchingActive then
    StopSearch
  else
    Close;
end;

procedure TfrmFindDlg.cm_Cancel(const Params: array of string);
begin
  btnStop.Click;
end;

procedure TfrmFindDlg.cm_Close(const Params: array of string);
begin
  btnClose.Click;
end;

procedure TfrmFindDlg.cm_NewSearch(const Params: array of string);
begin
  btnNewSearch.Click;
end;

procedure TfrmFindDlg.cm_LastSearch(const Params: array of string);
begin
  btnLastSearch.Click;
end;

procedure TfrmFindDlg.cm_View(const Params: array of string);
begin
  btnView.Click;
end;

procedure TfrmFindDlg.cm_Edit(const Params: array of string);
begin
  btnEdit.Click;
end;

procedure TfrmFindDlg.cm_GoToFile(const Params: array of string);
begin
  btnGoToPath.Click;
end;

procedure TfrmFindDlg.cm_FeedToListbox(const Params: array of string);
begin
  btnWorkWithFound.Click;
end;

procedure TfrmFindDlg.cm_PageStandard(const Params: array of string);
begin
  pgcSearch.PageIndex:=0;
end;

procedure TfrmFindDlg.cm_PageAdvanced(const Params: array of string);
begin
  pgcSearch.PageIndex:=1;
end;

procedure TfrmFindDlg.cm_PagePlugins(const Params: array of string);
begin
  pgcSearch.PageIndex:=2;
end;

procedure TfrmFindDlg.cm_PageLoadSave(const Params: array of string);
begin
  pgcSearch.PageIndex:=3;
end;

procedure TfrmFindDlg.cm_PageResults(const Params: array of string);
begin
  pgcSearch.PageIndex:=4;
end;

procedure TfrmFindDlg.btnStopClick(Sender: TObject);
begin
  StopSearch;
//  AfterSearchStopped;
//  btnNewSearch.SetFocus;
end;

procedure TfrmFindDlg.FormCloseQuery(Sender: TObject;var CanClose: Boolean);
// was on F7
begin
  if FFindThread<>nil then  // we can't call StopSearch because it method will set focus on unavailable field
  begin
    FFindThread.OnTerminate:=nil;
    FFindThread.Terminate;
    FUpdateTimer.OnTimer(FUpdateTimer);
    FUpdateTimer.Enabled := False;
    FFindThread := nil;
  end;

  AfterSearchStopped;
  {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
    btnStart.Default := True;
  {$ENDIF}
  CanClose:= not Assigned(FFindThread);
end;

procedure TfrmFindDlg.btnCloseClick(Sender: TObject);
begin
  Close;
end;

procedure TfrmFindDlg.FormDestroy(Sender: TObject);
begin
  FreeThenNil(FoundedStringCopy);
  FreeThenNil(DsxPlugins);
end;

procedure TfrmFindDlg.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  {
  case Key of
{$IF DEFINED(LCLGTK) or DEFINED(LCLGTK2)}
    // On LCLGTK2 default button on Enter does not work.
    VK_RETURN, VK_SELECT:
      begin
        Key := 0;
        if btnStart.Enabled then
          btnStart.Click
        else
          btnStop.Click;
      end;
{$ENDIF}
    VK_ESCAPE:
      begin
        Key := 0;
        if FSearchingActive then
          StopSearch
        else
          Close;
      end;
    VK_1..VK_5:
      begin
        if Shift * KeyModifiersShortcut = [ssAlt] then
          begin
            pgcSearch.PageIndex := Key - VK_1;
            Key := 0;
          end;
      end;

    VK_TAB:
      begin
        if Shift * KeyModifiersShortcut = [ssCtrl] then
        begin
          pgcSearch.SelectNextPage(True);
          Key := 0;
        end
        else if Shift * KeyModifiersShortcut = [ssCtrl, ssShift] then
        begin
          pgcSearch.SelectNextPage(False);
          Key := 0;
        end;
      end;

    VK_F7 :
      begin
        if FFindThread<>nil then
        begin
          FFindThread.OnTerminate:=nil;
          FFindThread.Terminate;
          FUpdateTimer.OnTimer(FUpdateTimer);
          FUpdateTimer.Enabled := False;
          FFindThread := nil;
        end;

        AfterSearchStopped;
        {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
          btnStart.Default := True;
        {$ENDIF}

        if cmbFindText.Focused then // if F7 on already focused textSearch field- disable text search and set focun on file mask
        begin
          cbFindText.Checked:=False;
          cmbFindFileMask.SetFocus;
          cmbFindFileMask.SelectAll;
          exit;
        end else
        begin
          pgcSearch.PageIndex:= 0;
          cbFindText.Checked:=True;
          cmbFindText.SetFocus;
          cmbFindText.SelectAll;
        end;
      end;
  end;
  }
end;

procedure TfrmFindDlg.frmFindDlgClose(Sender: TObject;
  var CloseAction: TCloseAction);
begin
  glsMaskHistory.Assign(cmbFindFileMask.Items);
  glsSearchExcludeFiles.Assign(cmbExcludeFiles.Items);
  glsSearchExcludeDirectories.Assign(cmbExcludeDirectories.Items);

  if Assigned(FFrmAttributesEdit) then
  begin
    FFrmAttributesEdit.Close;
    FreeAndNil(FFrmAttributesEdit);
  end;
end;

procedure TfrmFindDlg.frmFindDlgShow(Sender: TObject);
var
  I: Integer;
begin

  pgcSearch.PageIndex:= 0;

  if cmbFindFileMask.Visible then
    cmbFindFileMask.SelectAll;

  cmbFindFileMask.Items.Assign(glsMaskHistory);
  cmbFindText.Items.Assign(glsSearchHistory);
  // if we already search text then use last searched text
  if not gFirstTextSearch then
    begin
      if glsSearchHistory.Count > 0 then
        cmbFindText.Text:= glsSearchHistory[0];
    end;
  cmbReplaceText.Items.Assign(glsReplaceHistory);
  cmbExcludeFiles.Items.Assign(glsSearchExcludeFiles);
  cmbExcludeDirectories.Items.Assign(glsSearchExcludeDirectories);

  cbFindText.Checked := False;
  lsFoundedFiles.Canvas.Font := lsFoundedFiles.Font;

  cmbPlugin.Clear;
  for I:= 0 to DSXPlugins.Count-1 do
    begin
      cmbPlugin.AddItem(DSXPlugins.GetDSXModule(i).Name+' (' + DSXPlugins.GetDSXModule(I).Descr+' )',nil);
    end;
  if (cmbPlugin.Items.Count>0) then cmbPlugin.ItemIndex:=0;

  if pgcSearch.ActivePage = tsStandard then
    if cmbFindFileMask.CanFocus then
      cmbFindFileMask.SetFocus;

  cbSelectedFiles.Checked := FSelectedFiles.Count > 0;
  cbSelectedFiles.Enabled := cbSelectedFiles.Checked;
end;

procedure TfrmFindDlg.gbDirectoriesResize(Sender: TObject);
begin
  pnlDirectoriesDepth.Width := gbDirectories.Width div 3;
end;

procedure TfrmFindDlg.lbSearchTemplatesSelectionChange(Sender: TObject; User: boolean);
begin
  if lbSearchTemplates.ItemIndex < 0 then
    lblSearchContents.Caption := ''
  else
  begin
    with gSearchTemplateList.Templates[lbSearchTemplates.ItemIndex].SearchRecord do
    begin
      if StartPath <> '' then
        lblSearchContents.Caption := '"' + FilesMasks + '" -> "' + StartPath + '"'
      else
        lblSearchContents.Caption := '"' + FilesMasks + '"';
    end;
  end;
end;

procedure TfrmFindDlg.LoadSelectedTemplate;
var
  SearchTemplate: TSearchTemplate;
begin
  if lbSearchTemplates.ItemIndex < 0 then Exit;
  SearchTemplate:= gSearchTemplateList.Templates[lbSearchTemplates.ItemIndex];
  if Assigned(SearchTemplate) then
  begin
    FLastTemplateName := SearchTemplate.TemplateName;
    LoadTemplate(SearchTemplate.SearchRecord);
  end;
end;

procedure TfrmFindDlg.LoadTemplate(const Template: TSearchTemplateRec);
begin
  with Template do
  begin
    if StartPath <> '' then
      edtFindPathStart.Text:= StartPath;
    cmbExcludeDirectories.Text:= ExcludeDirectories;
    cmbFindFileMask.Text:= FilesMasks;
    cmbExcludeFiles.Text:= ExcludeFiles;
    if (SearchDepth + 1 >= 0) and (SearchDepth + 1 < cmbSearchDepth.Items.Count) then
      cmbSearchDepth.ItemIndex:= SearchDepth + 1
    else
      cmbSearchDepth.ItemIndex:= 0;
    cbRegExp.Checked := RegExp;
    cbPartialNameSearch.Checked := IsPartialNameSearch;
    cbFollowSymLinks.Checked := FollowSymLinks;
    cbFindInArchive.Checked := FindInArchives;
    // attributes
    edtAttrib.Text:= AttributesPattern;
    // file date/time
    cbDateFrom.Checked:= IsDateFrom;
    if IsDateFrom then
      ZVDateFrom.Date:= DateTimeFrom;

    cbDateTo.Checked:= IsDateTo;
    if IsDateTo then
      ZVDateTo.Date:= DateTimeTo;

    cbTimeFrom.Checked:= IsTimeFrom;
    if IsTimeFrom then
      ZVTimeFrom.Time:= DateTimeFrom;

    cbTimeTo.Checked:= IsTimeTo;
    if IsTimeTo then
      ZVTimeTo.Time:= DateTimeTo;

    // not older then
    cbNotOlderThan.Checked:= IsNotOlderThan;
    seNotOlderThan.Value:= NotOlderThan;
    cmbNotOlderThanUnit.ItemIndex := TimeUnitToComboIndex[NotOlderThanUnit];
    // file size
    cbFileSizeFrom.Checked:= IsFileSizeFrom;
    cbFileSizeTo.Checked:= IsFileSizeTo;
    seFileSizeFrom.Value:= FileSizeFrom;
    seFileSizeTo.Value:= FileSizeTo;
    cmbFileSizeUnit.ItemIndex := FileSizeUnitToComboIndex[FileSizeUnit];
    // find/replace text
    cbFindText.Checked:= IsFindText;
    cmbFindText.Text:= FindText;
    cbReplaceText.Checked:= IsReplaceText;
    cmbReplaceText.Text:= ReplaceText;
    cbCaseSens.Checked:= CaseSensitive;
    cbNotContainingText.Checked:= NotContainingText;
    cbTextRegExp.Checked:= TextRegExp;
    cmbEncoding.Text:= TextEncoding;
    // plugins
    cmbPlugin.Text:= SearchPlugin;
    frmContentPlugins.Load(Template);
  end;
end;

procedure TfrmFindDlg.lsFoundedFilesDblClick(Sender: TObject);
begin
//  if not FSearchingActive then btnGoToPathClick(Sender);
  btnGoToPathClick(Sender);
end;

procedure TfrmFindDlg.lsFoundedFilesKeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if (Shift = []) and (lsFoundedFiles.ItemIndex <> -1) then
  begin
    case Key of
      VK_F3:
      begin
        ShowViewerByGlob(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]);
        Key := 0;
      end;

      VK_F4:
      begin
        ShowEditorByGlob(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]);
        Key := 0;
      end;

      VK_DELETE:
      begin
        miRemoveFromLlistClick(Sender);
        Key := 0;
      end;

      VK_RETURN:
      begin
        if not FSearchingActive then
        begin
          btnGoToPathClick(Sender);
          Key := 0;
        end;
      end;

      VK_RIGHT,VK_LEFT:
      begin
        if not FSearchingActive then
        begin
          if FRButtonPanelSender<>nil then (FRButtonPanelSender as TButton).SetFocus
          else btnNewSearch.SetFocus;
//          btnNewSearch.SetFocus;
          Key := 0;
        end else
        begin
          Key := 0;
          btnStop.SetFocus;
        end;
      end;


    end;

  end;
end;

procedure TfrmFindDlg.lsFoundedFilesMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i:integer;
  AFile: TFile;
  AFiles: TFiles;
  APoint: TPoint;
begin
  i:=lsFoundedFiles.ItemAtPos(Point(X,Y),False);

  if (i>=0) then
  begin
    LastClickResultsPath:=GetDeepestExistingPath(lsFoundedFiles.Items[i]);

    if (Button=mbRight)and(lsFoundedFiles.Selected[i]<>True) then
    begin
         lsFoundedFiles.ClearSelection;
         lsFoundedFiles.Selected[i]:=True;
    end;
  end;
end;

procedure TfrmFindDlg.lsFoundedFilesMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i:integer;
  sPath:string;
  AFile: TFile;
  AFiles: TFiles;

  Panel:TFileView;
  FileList :TFileTree;

  pt: TPoint;
begin
  if Button=mbRight then
  begin

    if Shift=[ssCtrl] then    // Show System context menu
    begin

      {$IF DEFINED(MSWINDOWS)}
        try
          AFiles:= TFiles.Create(LastClickResultsPath);
          AFiles.Path:=LastClickResultsPath;

          i:=0;
          while i<lsFoundedFiles.Count do
          begin
           if lsFoundedFiles.Selected[i] then
           begin
             sPath:=lsFoundedFiles.Items[i];
             AFile:= TFileSystemFileSource.CreateFile(sPath);
             AFiles.Add(aFile);
           end;
          inc(i);
          end;

          try
            pt.X := X;
            pt.Y := Y;
            pt := ClientToScreen(pt);
            ShowContextMenu(lsFoundedFiles, AFiles, pt.X, pt.Y, False, nil);
          finally
            FreeAndNil(AFiles);
          end;


        except
          on E: EContextMenuException do
            ShowException(E)
          else;
        end;

      {$ENDIF}

    end else
    begin
      PopupMenuFind.PopUp;  // Show DC menu
    end;

  end;
end;

procedure TfrmFindDlg.lsFoundedFilesMouseWheelDown(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  if (Shift=[ssCtrl])and(gFonts[dcfEditor].Size<MAX_FONT_SIZE_EDITOR) then
  begin
    //gFonts[dcfEditor].Size:=gFonts[dcfEditor].Size+1;
    //FontOptionsToFont(gFonts[dcfEditor], Editor.Font);

    lsFoundedFiles.Font.Size:=lsFoundedFiles.Font.Size-1;
    Handled:=True;
  end;
end;

procedure TfrmFindDlg.lsFoundedFilesMouseWheelUp(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  if (Shift=[ssCtrl])and(gFonts[dcfEditor].Size<MAX_FONT_SIZE_EDITOR) then
  begin
    //gFonts[dcfFileSearchResults].Size:=gFonts[dcfFileSearchResults].Size+1;
    //FontOptionsToFont(gFonts[dcfEditor], Editor.Font);

    lsFoundedFiles.Font.Size:=lsFoundedFiles.Font.Size+1;
    Handled:=True;
  end;
end;

procedure TfrmFindDlg.miOpenInNewTabClick(Sender: TObject);
var
  i,ind:integer;
  sPath:string;

  Notebook: TFileViewNotebook;
  NewPage: TFileViewPage;

begin
  ind:=lsFoundedFiles.ItemIndex;
  Notebook := frmMain.ActiveNotebook;

  i:=0;
  while i<lsFoundedFiles.Count do
  begin
    if lsFoundedFiles.Selected[i] then
    begin
      sPath :=lsFoundedFiles.Items[i];
      sPath := GetDeepestExistingPath(sPath);

      NewPage := Notebook.NewPage(Notebook.ActiveView);
      NewPage.FileView.CurrentPath:=sPath;
      NewPage.FileView.SetActiveFile(ExtractFileName(lsFoundedFiles.Items[i]));
    end;
  inc(i);
  end;

end;

procedure TfrmFindDlg.miRemoveFromLlistClick(Sender: TObject);
var
  i:Integer;
begin
  if lsFoundedFiles.ItemIndex=-1 then Exit;
  if lsFoundedFiles.SelCount = 0 then Exit;

  for i:=lsFoundedFiles.Items.Count-1 downto 0 do
    if lsFoundedFiles.Selected[i] then
      lsFoundedFiles.Items.Delete(i);

  miShowAllFound.Enabled:=True;
end;

procedure TfrmFindDlg.miShowAllFoundClick(Sender: TObject);
begin
  lsFoundedFiles.Clear;
  lsFoundedFiles.Items.AddStrings(FoundedStringCopy);

  miShowAllFound.Enabled:=False;
end;

procedure TfrmFindDlg.miShowInEditorClick(Sender: TObject);
begin
  if lsFoundedFiles.ItemIndex>=0 then
     ShowEditorByGlob(lsFoundedFiles.Items[lsFoundedFiles.ItemIndex]);
end;

procedure TfrmFindDlg.miShowInViewerClick(Sender: TObject);
var
  sl:TStringList;
  i:Integer;
begin
  if lsFoundedFiles.ItemIndex=-1 then Exit;

  sl:=TStringList.Create;
  try
    for i:=0 to lsFoundedFiles.Items.Count-1 do
      if lsFoundedFiles.Selected[i] then
        sl.Add(lsFoundedFiles.Items[i]);
    ShowViewer(sl);
  finally
    sl.Free;
  end;
end;

procedure TfrmFindDlg.seFileSizeFromChange(Sender: TObject);
begin
  if not FUpdating then
    cbFileSizeFrom.Checked:= (seFileSizeFrom.Value > 0);
end;

procedure TfrmFindDlg.seFileSizeToChange(Sender: TObject);
begin
  if not FUpdating then
    cbFileSizeTo.Checked:= (seFileSizeTo.Value > 0);
end;

procedure TfrmFindDlg.SelectTemplate(const ATemplateName: String);
var
  i: Integer;
begin
  for i := 0 to lbSearchTemplates.Count - 1 do
    if lbSearchTemplates.Items[i] = ATemplateName then
    begin
      lbSearchTemplates.ItemIndex := i;
      Break;
    end;
end;

procedure TfrmFindDlg.seNotOlderThanChange(Sender: TObject);
begin
  if not FUpdating then
    cbNotOlderThan.Checked:= (seNotOlderThan.Value > 0);
end;

procedure TfrmFindDlg.tsLoadSaveShow(Sender: TObject);
begin
  UpdateTemplatesList;
  if (lbSearchTemplates.Count > 0) and (lbSearchTemplates.ItemIndex = -1) then
    lbSearchTemplates.ItemIndex := 0;
end;

procedure TfrmFindDlg.tsStandardEnter(Sender: TObject);
begin
  {$IF NOT (DEFINED(LCLGTK) or DEFINED(LCLGTK2))}
    btnStart.Default:= True;
  {$ENDIF}
end;

procedure TfrmFindDlg.UpdateTemplatesList;
var
  OldIndex: Integer;
begin
  OldIndex := lbSearchTemplates.ItemIndex;
  gSearchTemplateList.LoadToStringList(lbSearchTemplates.Items);
  if OldIndex <> -1 then
    lbSearchTemplates.ItemIndex := OldIndex;
end;

procedure TfrmFindDlg.OnUpdateTimer(Sender: TObject);
begin
  if Assigned(FFindThread) then
  begin
    lblStatus.Caption := Format(rsFindScanned, [FFindThread.FilesScanned])+FTimeSearch;
    lblFound.Caption := Format(rsFindFound, [FFindThread.FilesFound]);
    lblCurrent.Caption := rsFindScanning + ': ' + FFindThread.CurrentDir;
  end;
end;

procedure TfrmFindDlg.ZVDateFromChange(Sender: TObject);
begin
  if not FUpdating then
    cbDateFrom.Checked:= True;
end;

procedure TfrmFindDlg.ZVDateToChange(Sender: TObject);
begin
  if not FUpdating then
    cbDateTo.Checked:= True;
end;

procedure TfrmFindDlg.ZVTimeFromChange(Sender: TObject);
begin
  if not FUpdating then
    cbTimeFrom.Checked:= True;
end;

procedure TfrmFindDlg.ZVTimeToChange(Sender: TObject);
begin
  if not FUpdating then
    cbTimeTo.Checked:= True;
end;

procedure TfrmFindDlg.OnAddAttribute(Sender: TObject);
var
  sAttr: String;
begin
  sAttr := edtAttrib.Text;
  if edtAttrib.SelStart > 0 then
    // Insert at caret position.
    Insert((Sender as TfrmAttributesEdit).AttrsAsText, sAttr, edtAttrib.SelStart + 1)
  else
    sAttr := sAttr + (Sender as TfrmAttributesEdit).AttrsAsText;
  edtAttrib.Text := sAttr;
end;

function TfrmFindDlg.InvalidRegExpr(AChecked: Boolean; const ARegExpr: String): Boolean;
var
  sMsg: String;
begin
  Result:= False;
  if AChecked then
  try
    ExecRegExpr(ARegExpr, '');
  except
    on E: Exception do
    begin
      Result:= True;
      sMsg:= StringReplace(cbRegExp.Caption, '&', '', [rfReplaceAll]);
      MessageDlg(sMsg + ': ' +  E.Message, mtError, [mbOK], 0);
    end;
  end;
end;

procedure TfrmFindDlg.pgcSearchChange(Sender: TObject);
begin
  if (pgcSearch.ActivePage = tsStandard) and not cmbFindFileMask.Focused then
  begin
    if cmbFindFileMask.CanFocus then
      cmbFindFileMask.SetFocus;
  end;
end;

procedure TfrmFindDlg.SaveTemplate(SaveStartingPath: Boolean);
var
  sName: String;
  SearchTemplate: TSearchTemplate;
  SearchRec: TSearchTemplateRec;
begin
  if InvalidRegExpr(cbRegExp.Checked, cmbFindFileMask.Text) or
     InvalidRegExpr(cbTextRegExp.Checked, cmbFindText.Text) then
    Exit;

  sName := FLastTemplateName;
  if not InputQuery(rsFindSaveTemplateCaption, rsFindSaveTemplateTitle, sName) then
  begin
    ModalResult:= mrCancel;
    Exit;
  end;

  FLastTemplateName := sName;
  SearchTemplate := gSearchTemplateList.TemplateByName[sName];
  if Assigned(SearchTemplate) then
  begin
    // TODO: Ask for overwriting existing template.
    FillFindOptions(SearchRec, SaveStartingPath);
    SearchTemplate.SearchRecord := SearchRec;
    Exit;
  end;

  SearchTemplate:= TSearchTemplate.Create;
  try
    SearchTemplate.TemplateName:= sName;
    FillFindOptions(SearchRec, SaveStartingPath);
    SearchTemplate.SearchRecord := SearchRec;
    gSearchTemplateList.Add(SearchTemplate);
  except
    FreeAndNil(SearchTemplate);
    raise;
  end;
  UpdateTemplatesList;
  SelectTemplate(FLastTemplateName);
end;


initialization
  TFormCommands.RegisterCommandsForm(TfrmFindDlg, HotkeysCategory, @rsHotkeyCategoryFindFiles);



finalization
  FreeAndNil(GfrmFindDlgInstance);

end.
