lazarus/ide/findreplacedialog.pp

554 lines
17 KiB
ObjectPascal

{
/***************************************************************************
findreplacedialog.pp
--------------------
***************************************************************************/
Author: Mattias Gaertner
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Abstract:
Find and replace dialog form.
Usage:
Add to program
"Application.CreateForm(TLazFindReplaceDialog, FindReplaceDlg);"
Set the FindReplaceDlg.Options property
then do MResult:=FindReplaceDlg.ShowModal
ShowModal can have three possible results:
- mrOk for Find/Replace.
- mrAll for ReplaceAll
- mrCancel for Cancel
}
unit FindReplaceDialog;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, RegExpr,
LCLType, Controls, StdCtrls, Forms, Buttons, ExtCtrls, Dialogs, Graphics, ButtonPanel,
SynEditTypes, SynEdit,
IDEHelpIntf, IDEImagesIntf, IDEWindowIntf, IDEDialogs, InputHistory,
EnvironmentOpts,
LazarusIdeStrConsts;
type
TFindDlgComponent = (fdcText, fdcReplace);
TOnFindDlgKey = procedure(Sender: TObject; var Key: Word; Shift:TShiftState;
FindDlgComponent: TFindDlgComponent) of Object;
TLazFindReplaceState = record
FindText: string;
ReplaceText: string;
Options: TSynSearchOptions;
end;
{ TLazFindReplaceDialog }
TLazFindReplaceDialog = class(TForm)
BackwardRadioButton: TRadioButton;
BtnPanel: TButtonPanel;
CaseSensitiveCheckBox: TCheckBox;
DirectionGroupBox: TGroupBox;
EntireScopeRadioButton: TRadioButton;
ForwardRadioButton: TRadioButton;
FromCursorRadioButton: TRadioButton;
GlobalRadioButton: TRadioButton;
MultiLineCheckBox: TCheckBox;
OptionsGroupBox: TGroupBox;
OriginGroupBox: TGroupBox;
PromptOnReplaceCheckBox: TCheckBox;
RegularExpressionsCheckBox: TCheckBox;
ReplaceTextComboBox: TComboBox;
ReplaceWithCheckbox: TCheckBox;
ScopeGroupBox: TGroupBox;
SelectedRadioButton: TRadioButton;
EnableAutoCompleteSpeedButton: TSpeedButton;
TextToFindComboBox: TComboBox;
TextToFindLabel: TLabel;
WholeWordsOnlyCheckBox: TCheckBox;
procedure EnableAutoCompleteSpeedButtonClick(Sender: TObject);
procedure FormChangeBounds(Sender: TObject);
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormShow(Sender: TObject);
procedure HelpButtonClick(Sender: TObject);
procedure OptionsGroupBoxResize(Sender: TObject);
procedure ReplaceWithCheckboxChange(Sender: TObject);
procedure TextToFindComboboxKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure OkButtonClick(Sender: TObject);
procedure ReplaceAllButtonClick(Sender: TObject);
procedure CancelButtonClick(Sender: TObject);
private
FOnKey: TOnFindDlgKey;
fReplaceAllClickedLast: boolean;
RegExpr: TRegExpr;
DlgHistoryIndex: array[TFindDlgComponent] of integer;
DlgUserText: array[TFindDlgComponent] of string;
function CheckInput: boolean;
function GetComponentText(c: TFindDlgComponent): string;
function GetEnableAutoComplete: boolean;
procedure SetComponentText(c: TFindDlgComponent; const AValue: string);
procedure SetEnableAutoComplete(const AValue: boolean);
procedure SetOnKey(const AValue: TOnFindDlgKey);
procedure SetOptions(NewOptions: TSynSearchOptions);
function GetOptions: TSynSearchOptions;
function GetFindText: AnsiString;
procedure SetFindText(const NewFindText: AnsiString);
function GetReplaceText: AnsiString;
procedure SetReplaceText(const NewReplaceText: AnsiString);
procedure SetComboBoxText(AComboBox: TComboBox; const AText: AnsiString);
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure UpdateHints;
procedure ResetUserHistory;
procedure RestoreState(const AState: TLazFindReplaceState);
procedure SaveState(out AState: TLazFindReplaceState);
public
property Options: TSynSearchOptions read GetOptions write SetOptions;
property EnableAutoComplete: boolean read GetEnableAutoComplete
write SetEnableAutoComplete;
property FindText:AnsiString read GetFindText write SetFindText;
property ReplaceText:AnsiString read GetReplaceText write SetReplaceText;
property OnKey: TOnFindDlgKey read FOnKey write SetOnKey;
property ComponentText[c: TFindDlgComponent]: string
read GetComponentText write SetComponentText;
end;
var
LazFindReplaceDialog: TLazFindReplaceDialog = nil;
implementation
{$R *.lfm}
{ TLazFindReplaceDialog }
constructor TLazFindReplaceDialog.Create(TheOwner:TComponent);
begin
inherited Create(TheOwner);
Caption:='';
TextToFindComboBox.Text:='';
TextToFindLabel.Caption:=dlgTextToFind;
ReplaceTextComboBox.Text:='';
ReplaceWithCheckbox.Caption:=dlgReplaceWith;
IDEImages.AssignImage(EnableAutoCompleteSpeedButton, 'autocomplete');
OptionsGroupBox.Caption:=lisOptions;
with CaseSensitiveCheckBox do begin
Caption:=dlgCaseSensitive;
Hint:=lisDistinguishBigAndSmallLettersEGAAndA;
end;
with WholeWordsOnlyCheckBox do begin
Caption:=dlgWholeWordsOnly;
Hint:=lisOnlySearchForWholeWords;
end;
with RegularExpressionsCheckBox do begin
Caption:=dlgRegularExpressions;
Hint:=lisActivateRegularExpressionSyntaxForTextAndReplaceme;
end;
with MultiLineCheckBox do begin
Caption:=lisFindFileMultiLinePattern;
Hint:=lisAllowSearchingForMultipleLines;
end;
with PromptOnReplaceCheckBox do begin
Caption:=dlgPromptOnReplace;
Hint:=lisAskBeforeReplacingEachFoundText;
end;
OriginGroupBox.Caption := dlgSROrigin;
FromCursorRadioButton.Caption := dlgFromCursor;
EntireScopeRadioButton.Caption := dlgFromBeginning;
ScopeGroupBox.Caption := dlgSearchScope;
GlobalRadioButton.Caption := dlgGlobal;
SelectedRadioButton.Caption := dlgSelectedText;
DirectionGroupBox.Caption := dlgDirection;
ForwardRadioButton.Caption := lisFRForwardSearch;
BackwardRadioButton.Caption := lisFRBackwardSearch;
// CloseButton works now as ReplaceAllButton
BtnPanel.CloseButton.Caption := dlgReplaceAll;
IDEImages.AssignImage(BtnPanel.CloseButton, 'btn_all');
fReplaceAllClickedLast:=false;
UpdateHints;
AutoSize:=IDEDialogLayoutList.Find(Self,false)=nil;
IDEDialogLayoutList.ApplyLayout(Self);
end;
destructor TLazFindReplaceDialog.Destroy;
begin
RegExpr.Free;
inherited Destroy;
if LazFindReplaceDialog=Self then
LazFindReplaceDialog:=nil;
end;
procedure TLazFindReplaceDialog.FormClose(Sender: TObject;
var CloseAction: TCloseAction);
begin
IDEDialogLayoutList.SaveLayout(Self);
end;
procedure TLazFindReplaceDialog.FormShow(Sender: TObject);
begin
TextToFindComboBox.DropDownCount:=EnvironmentOptions.DropDownCount;
ReplaceTextComboBox.DropDownCount:=EnvironmentOptions.DropDownCount;
end;
procedure TLazFindReplaceDialog.UpdateHints;
begin
if EnableAutoComplete then
EnableAutoCompleteSpeedButton.Hint:=lisAutoCompletionOn
else
EnableAutoCompleteSpeedButton.Hint:=lisAutoCompletionOff;
end;
procedure TLazFindReplaceDialog.ResetUserHistory;
var
c: TFindDlgComponent;
begin
for c := Low(TFindDlgComponent) to High(TFindDlgComponent) do
DlgHistoryIndex[c] := -1;
end;
procedure TLazFindReplaceDialog.RestoreState(const AState: TLazFindReplaceState);
begin
Options:=AState.Options;
FindText:=AState.FindText;
ReplaceText:=AState.ReplaceText;
end;
procedure TLazFindReplaceDialog.SaveState(out AState: TLazFindReplaceState);
begin
FillChar(AState{%H-}, SizeOf(TLazFindReplaceState), 0);
AState.Options:=Options;
AState.FindText:=FindText;
AState.ReplaceText:=ReplaceText;
end;
procedure TLazFindReplaceDialog.TextToFindComboboxKeyDown(Sender: TObject;
var Key: Word; Shift: TShiftState);
var
Component: TFindDlgComponent;
HistoryList: TStringList;
CurText: string;
CurIndex: integer;
procedure SetHistoryText;
var s: string;
begin
if DlgHistoryIndex[Component]>=0 then
s := HistoryList[DlgHistoryIndex[Component]]
else
s := DlgUserText[Component];
//debugln(' SetHistoryText "',s,'"');
ComponentText[Component]:=s
end;
procedure FetchFocus;
begin
if Sender is TWinControl then
TWinControl(Sender).SetFocus;
end;
begin
//debugln('TLazFindReplaceDialog.TextToFindComboBoxKeyDown Key=',Key,' RETURN=',VK_RETURN,' TAB=',VK_TAB,' DOWN=',VK_DOWN,' UP=',VK_UP);
if Sender=TextToFindComboBox then
Component:=fdcText
else
Component:=fdcReplace;
if Assigned(OnKey) then begin
OnKey(Sender, Key, Shift, Component);
end
else
begin
if Component=fdcText then
HistoryList:= InputHistories.FindHistory
else
HistoryList:= InputHistories.ReplaceHistory;
CurIndex := DlgHistoryIndex[Component];
CurText := ComponentText[Component];
if Key=VK_UP then begin
// go forward in history
if CurIndex >= 0 then begin
if (HistoryList[CurIndex] <> CurText) then
DlgUserText[Component] := CurText; // save user text
dec(DlgHistoryIndex[Component]);
SetHistoryText;
end;
FetchFocus;
Key:=VK_UNKNOWN;
end
else if Key=VK_DOWN then begin
// go back in history
if (CurIndex<0)
or (HistoryList[CurIndex] <> CurText) then
DlgUserText[Component] := CurText; // save user text
if CurIndex < HistoryList.Count-1 then
begin
inc(DlgHistoryIndex[Component]);
SetHistoryText;
end;
FetchFocus;
Key:=VK_UNKNOWN;
end;
end;
end;
procedure TLazFindReplaceDialog.HelpButtonClick(Sender: TObject);
begin
LazarusHelp.ShowHelpForIDEControl(Self);
end;
procedure TLazFindReplaceDialog.OptionsGroupBoxResize(Sender: TObject);
var
h: integer;
begin
DisableAlign;
h := (OptionsGroupBox.Height-12) div 3;
OriginGroupBox.Height := h;
ScopeGroupBox.Height := h;
DirectionGroupBox.Height := OptionsGroupBox.Height-2*h-12;
EnableAlign;
end;
procedure TLazFindReplaceDialog.ReplaceWithCheckboxChange(Sender: TObject);
begin
if ReplaceWithCheckbox.Checked then
BtnPanel.ShowButtons := BtnPanel.ShowButtons + [pbClose]
else
BtnPanel.ShowButtons := BtnPanel.ShowButtons - [pbClose];
ReplaceTextComboBox.Enabled:=ReplaceWithCheckbox.Checked;
PromptOnReplaceCheckBox.Enabled:=ReplaceWithCheckbox.Checked;
if ReplaceWithCheckbox.Checked then
begin
Caption := lisReplace;
BtnPanel.OKButton.Caption := lisBtnReplace;
end else
begin
Caption := lisMenuFind;
BtnPanel.OKButton.Caption := lisBtnFind;
end;
end;
procedure TLazFindReplaceDialog.FormChangeBounds(Sender: TObject);
var
w: integer;
begin
DisableAlign;
w := (ClientWidth - 18) div 2;
OptionsGroupBox.Width := w;
OriginGroupBox.Width := w;
ScopeGroupBox.Width := w;
DirectionGroupBox.Width := w;
EnableAlign;
end;
procedure TLazFindReplaceDialog.EnableAutoCompleteSpeedButtonClick(
Sender: TObject);
begin
TextToFindComboBox.AutoComplete:=EnableAutoCompleteSpeedButton.Down;
ReplaceTextComboBox.AutoComplete:=EnableAutoCompleteSpeedButton.Down;
UpdateHints;
end;
procedure TLazFindReplaceDialog.OkButtonClick(Sender:TObject);
begin
if not CheckInput then exit;
fReplaceAllClickedLast:=false;
ActiveControl:=TextToFindComboBox;
ModalResult:=mrOk;
end;
procedure TLazFindReplaceDialog.ReplaceAllButtonClick(Sender:TObject);
begin
if not CheckInput then exit;
fReplaceAllClickedLast:=true;
ActiveControl:=TextToFindComboBox;
ModalResult:=mrAll;
end;
procedure TLazFindReplaceDialog.CancelButtonClick(Sender:TObject);
begin
ActiveControl:=TextToFindComboBox;
end;
function TLazFindReplaceDialog.CheckInput: boolean;
begin
Result:=false;
if RegularExpressionsCheckBox.Checked then begin
if RegExpr=nil then RegExpr:=TRegExpr.Create;
try
RegExpr.Expression:=FindText;
RegExpr.Exec('test');
except
on E: ERegExpr do begin
IDEMessageDialog(lisUEErrorInRegularExpression,
E.Message,mtError,[mbCancel]);
exit;
end;
end;
if ReplaceTextComboBox.Enabled then begin
try
RegExpr.Substitute(ReplaceText);
except
on E: ERegExpr do begin
IDEMessageDialog(lisUEErrorInRegularExpression,
E.Message,mtError,[mbCancel]);
exit;
end;
end;
end;
end;
Result:=true;
end;
function TLazFindReplaceDialog.GetComponentText(c: TFindDlgComponent): string;
begin
case c of
fdcText: Result:=FindText;
else
Result:=Replacetext;
end;
end;
function TLazFindReplaceDialog.GetEnableAutoComplete: boolean;
begin
Result:=EnableAutoCompleteSpeedButton.Down;
end;
procedure TLazFindReplaceDialog.SetComponentText(c: TFindDlgComponent;
const AValue: string);
begin
case c of
fdcText: FindText:=AValue;
else
Replacetext:=AValue;
end;
end;
procedure TLazFindReplaceDialog.SetEnableAutoComplete(const AValue: boolean);
begin
EnableAutoCompleteSpeedButton.Down:=AValue;
TextToFindComboBox.AutoComplete:=AValue;
ReplaceTextComboBox.AutoComplete:=AValue;
UpdateHints;
end;
procedure TLazFindReplaceDialog.SetOnKey(const AValue: TOnFindDlgKey);
begin
FOnKey:=AValue;
end;
procedure TLazFindReplaceDialog.SetOptions(NewOptions:TSynSearchOptions);
begin
CaseSensitiveCheckBox.Checked:=ssoMatchCase in NewOptions;
WholeWordsOnlyCheckBox.Checked:=ssoWholeWord in NewOptions;
RegularExpressionsCheckBox.Checked:=ssoRegExpr in NewOptions;
MultiLineCheckBox.Checked:=ssoRegExprMultiLine in NewOptions;
PromptOnReplaceCheckBox.Checked:=ssoPrompt in NewOptions;
if ssoEntireScope in NewOptions
then EntireScopeRadioButton.Checked:=True
else FromCursorRadioButton.Checked:=True;
if ssoSelectedOnly in NewOptions
then SelectedRadioButton.Checked:=True
else GlobalRadioButton.Checked:=True;
if ssoBackwards in NewOptions
then BackwardRadioButton.Checked:=True
else ForwardRadioButton.Checked:=True;
if ssoReplace in NewOptions then
BtnPanel.ShowButtons := BtnPanel.ShowButtons + [pbClose]
else
BtnPanel.ShowButtons := BtnPanel.ShowButtons - [pbClose];
ReplaceWithCheckbox.Checked := ssoReplace in NewOptions;
ReplaceTextComboBox.Enabled := ssoReplace in NewOptions;
PromptOnReplaceCheckBox.Enabled := ssoReplace in NewOptions;
fReplaceAllClickedLast := ssoReplaceAll in NewOptions;
if ssoReplace in NewOptions then
begin
Caption := lisReplace;
BtnPanel.OKButton.Caption := lisBtnReplace;
end else
begin
Caption := lisMenuFind;
BtnPanel.OKButton.Caption := lisBtnFind;
end;
//DebugLn(['TLazFindReplaceDialog.SetOptions END ssoSelectedOnly=',ssoSelectedOnly in NewOptions,' SelectedRadioButton.Checked=',SelectedRadioButton.Checked]);
end;
function TLazFindReplaceDialog.GetOptions:TSynSearchOptions;
begin
Result:=[];
if CaseSensitiveCheckBox.Checked then Include(Result,ssoMatchCase);
if WholeWordsOnlyCheckBox.Checked then Include(Result,ssoWholeWord);
if RegularExpressionsCheckBox.Checked then Include(Result,ssoRegExpr);
if MultiLineCheckBox.Checked then Include(Result,ssoRegExprMultiLine);
if PromptOnReplaceCheckBox.Checked then Include(Result,ssoPrompt);
if EntireScopeRadioButton.Checked then Include(Result,ssoEntireScope);
if SelectedRadioButton.Checked then include(Result,ssoSelectedOnly);
if BackwardRadioButton.Checked then include(Result,ssoBackwards);
if pbClose in BtnPanel.ShowButtons then include(Result,ssoReplace);
if fReplaceAllClickedLast then include(Result,ssoReplaceAll);
end;
function TLazFindReplaceDialog.GetFindText:AnsiString;
begin
Result:=TextToFindComboBox.Text;
end;
procedure TLazFindReplaceDialog.SetFindText(const NewFindText: AnsiString);
begin
// SetComboBoxText(TextToFindComboBox,NewFindText);
TextToFindComboBox.Text:=NewFindText;
TextToFindComboBox.SelectAll;
//debugln('TLazFindReplaceDialog.SetFindText A TextToFindComboBox.SelStart=',dbgs(TextToFindComboBox.SelStart),' TextToFindComboBox.SelLength=',dbgs(TextToFindComboBox.SelLength),' TextToFindComboBox.Text="',TextToFindComboBox.Text,'" NewFindText="',DbgStr(NewFindText),'"');
end;
function TLazFindReplaceDialog.GetReplaceText:AnsiString;
begin
Result:=ReplaceTextComboBox.Text;
end;
procedure TLazFindReplaceDialog.SetReplaceText(const NewReplaceText:AnsiString);
begin
SetComboBoxText(ReplaceTextComboBox,NewReplaceText);
end;
procedure TLazFindReplaceDialog.SetComboBoxText(AComboBox:TComboBox;
const AText:AnsiString);
var a:integer;
begin
a:=AComboBox.Items.IndexOf(AText);
//debugln('TLazFindReplaceDialog.SetComboBoxText ',AText,' ',a);
if a>=0 then
AComboBox.ItemIndex:=a
else begin
AComboBox.Items.Add(AText);
AComboBox.ItemIndex:=AComboBox.Items.IndexOf(AText);
end;
end;
end.