{ /*************************************************************************** findreplacedialog.pp -------------------- ***************************************************************************/ Author: Mattias Gaertner ***************************************************************************** * * * See the file COPYING.modifiedLGPL, included in this distribution, * * for details about the copyright. * * * * 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. * * * ***************************************************************************** Abstract: Find and replace dialog form. Usage: Add to program "Application.CreateForm(TLazFindReplaceDialog, FindReplaceDlg);" Set the FindReplaceDlg.Options poperty 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, LCLProc, LCLType, Controls, StdCtrls, Forms, Buttons, ExtCtrls, LResources, Dialogs, SynEditTypes, SynRegExpr, SynEdit, LazarusIdeStrConsts; type TFindDlgComponent = (fdcText, fdcReplace); TOnFindDlgKey = procedure(Sender: TObject; var Key: Word; Shift:TShiftState; FindDlgComponent: TFindDlgComponent) of Object; TLazFindReplaceDialog = class(TForm) TextToFindLabel: TLabel; ReplaceWithLabel: TLabel; TextToFindComboBox: TComboBox; ReplaceTextComboBox: TComboBox; OptionsGroupBox: TGroupBox; CaseSensitiveCheckBox: TCheckBox; WholeWordsOnlyCheckBox: TCheckBox; RegularExpressionsCheckBox: TCheckBox; MultiLineCheckBox: TCheckBox; PromptOnReplaceCheckBox: TCheckBox; DirectionRadioGroup: TRadioGroup; ScopeRadioGroup: TRadioGroup; OriginRadioGroup: TRadioGroup; OkButton: TButton; ReplaceAllButton: TButton; CancelButton: TButton; procedure LazFindReplaceDialogResize(Sender: TObject); procedure OptionsGroupBoxResize(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; function CheckInput: boolean; function GetComponentText(c: TFindDlgComponent): string; procedure SetComponentText(c: TFindDlgComponent; const AValue: string); 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; public property Options:TSynSearchOptions read GetOptions write SetOptions; 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 FindReplaceDlg: TLazFindReplaceDialog; implementation { TLazFindReplaceDialog } constructor TLazFindReplaceDialog.Create(TheOwner:TComponent); var x: Integer; y: Integer; ComboX: Integer; OptionW: Integer; OptionsGrpW: Integer; OptionY: Integer; OptionH: Integer; begin inherited Create(TheOwner); Name:='LazFindReplaceDialog'; Caption:=''; Width:=400; Height:=300; BorderStyle:= bsDialog; Position:=poDesigned; OnResize:=@LazFindReplaceDialogResize; x:=4; y:=4; ComboX:=x+90; TextToFindComboBox:=TComboBox.Create(Self); with TextToFindComboBox do begin Name:='TextToFindComboBox'; SetBounds(ComboX,y,Self.ClientWidth-ComboX-x,Height); Anchors:= [akLeft, akTop, akRight]; Text:=''; OnKeyDown:=@TextToFindComboBoxKeyDown; Parent:=Self; TabOrder:=0; end; TextToFindLabel:=TLabel.Create(Self); with TextToFindLabel do begin Name:='TextToFindLabel'; SetBounds(x,y+3,ComboX-x,Height); Caption:=dlgTextToFing; FocusControl:= TextToFindComboBox; Parent:=Self; end; inc(y,TextToFindComboBox.Height+1); ReplaceTextComboBox:=TComboBox.Create(Self); with ReplaceTextComboBox do begin Name:='ReplaceTextComboBox'; SetBounds(ComboX,y,Self.ClientWidth-ComboX-x,Height); Anchors:= [akLeft, akTop, akRight]; Text:=''; OnKeyDown:=@TextToFindComboBoxKeyDown; Parent:=Self; TabOrder:=1; end; ReplaceWithLabel:=TLabel.Create(Self); with ReplaceWithLabel do begin Name:='ReplaceWithLabel'; SetBounds(x,y+3,ComboX-x,Height); Caption:=dlgReplaceWith; FocusControl:= ReplaceTextComboBox; Parent:=Self; end; inc(y,ReplaceTextComboBox.Height+1); OptionsGrpW:=(ClientWidth-15) div 2; x:=5; OptionsGroupBox:=TGroupBox.Create(Self); with OptionsGroupBox do begin Name:='OptionsGroupBox'; SetBounds(x,y,OptionsGrpW,160); Caption:=dlgFROpts; OnResize:=@OptionsGroupBoxResize; Parent:=Self; end; OptionH:=OptionsGroupBox.ClientHeight div 5; OptionW:=OptionsGroupBox.ClientWidth-12; OptionY:=2; CaseSensitiveCheckBox:=TCheckBox.Create(Self); with CaseSensitiveCheckBox do begin Name:='CaseSensitiveCheckBox'; Parent:=OptionsGroupBox; AutoSize := True; SetBounds(8,OptionY,OptionW,Height); Caption:=dlgCaseSensitive; Hint:=lisDistinguishBigAndSmallLettersEGAAndA; ShowHint:=true; inc(OptionY,OptionH); end; WholeWordsOnlyCheckBox:=TCheckBox.Create(Self); with WholeWordsOnlyCheckBox do begin Name:='WholeWordsOnlyCheckBox'; Parent:=OptionsGroupBox; AutoSize := False; SetBounds(8,OptionY,OptionW,Height); Caption:=dlgWholeWordsOnly; Hint:=lisOnlySearchForWholeWords; ShowHint:=true; inc(OptionY,OptionH); end; RegularExpressionsCheckBox:=TCheckBox.Create(Self); with RegularExpressionsCheckBox do begin Name:='RegularExpressionsCheckBox'; Parent:=OptionsGroupBox; AutoSize := False; SetBounds(8,OptionY,OptionW,Height); Caption:=dlgRegularExpressions; Hint:=lisActivateRegularExpressionSyntaxForTextAndReplaceme; ShowHint:=true; inc(OptionY,OptionH); end; MultiLineCheckBox:=TCheckBox.Create(Self); with MultiLineCheckBox do begin Name:='MultiLineCheckBox'; Parent:=OptionsGroupBox; AutoSize := False; SetBounds(8,OptionY,OptionW,Height); Caption:=dlgMultiLine; Enabled:=false; Hint:=lisAllowSearchingForMultipleLines; ShowHint:=true; inc(OptionY,OptionH); end; PromptOnReplaceCheckBox:=TCheckBox.Create(Self); with PromptOnReplaceCheckBox do begin Name:='PromptOnReplaceCheckBox'; Parent:=OptionsGroupBox; AutoSize := False; SetBounds(8,OptionY,OptionW,Height); Caption:=dlgPromptOnReplace; Checked:=true; Hint:=lisAskBeforeReplacingEachFoundText; ShowHint:=true; inc(OptionY,OptionH); end; OriginRadioGroup:=TRadioGroup.Create(Self); with OriginRadioGroup do begin Name:='OriginRadioGroup'; Parent:= Self; SetBounds(x+OptionsGrpW+x,OptionsGroupBox.Top,OptionsGrpW,65); Caption:=dlgSROrigin; with Items do begin BeginUpdate; Clear; Add(dlgFromCursor); Add(dlgEntireScope); EndUpdate; end; ItemIndex:=0; end; ScopeRadioGroup:=TRadioGroup.Create(Self); with ScopeRadioGroup do begin Name:='ScopeRadioGroup'; Parent:=Self; SetBounds(OriginRadioGroup.Left, OriginRadioGroup.Top+OriginRadioGroup.Height+5, OriginRadioGroup.Width,65); Caption:=dlgScope; with Items do begin BeginUpdate; Clear; Add(dlgGlobal); Add(dlgSelectedText); EndUpdate; end; ItemIndex:=0; end; DirectionRadioGroup:=TRadioGroup.Create(Self); with DirectionRadioGroup do begin Name:='DirectionRadioGroup'; Parent:=Self; SetBounds(OriginRadioGroup.Left, ScopeRadioGroup.Top+ScopeRadioGroup.Height+5, OriginRadioGroup.Width,65); Caption:=dlgDirection; with Items do begin BeginUpdate; Clear; Add(lisFRForwardSearch); Add(lisFRBackwardSearch); EndUpdate; end; ItemIndex:=0; end; OkButton:=TButton.Create(Self); with OkButton do begin Name:='OkButton'; Parent:= Self; Default:=true; SetBounds(Parent.ClientWidth-350,Parent.ClientHeight-32,120,Height); Caption:='Ok'; OnClick:=@OkButtonClick; end; ReplaceAllButton:=TButton.Create(Self); with ReplaceAllButton do begin Name:='ReplaceAllButton'; Parent:= Self; SetBounds(OkButton.Left+OkButton.Width+10,OkButton.Top,120,Height); Caption:=dlgReplaceAll; OnClick:=@ReplaceAllButtonClick; end; CancelButton:=TButton.Create(Self); with CancelButton do begin Name:='CancelButton'; Parent:= Self; Cancel:=true; SetBounds(ReplaceAllButton.Left+ReplaceAllButton.Width+10,OkButton.Top, 80,Height); Caption:=dlgCancel; OnClick:=@CancelButtonClick; end; fReplaceAllClickedLast:=false; ActiveControl:=TextToFindComboBox; end; destructor TLazFindReplaceDialog.Destroy; begin RegExpr.Free; inherited Destroy; end; procedure TLazFindReplaceDialog.TextToFindComboBoxKeyDown( Sender: TObject; var Key:Word; Shift:TShiftState); var Component: TFindDlgComponent; begin //debugln('TLazFindReplaceDialog.TextToFindComboBoxKeyDown Key=',Key,' RETURN=',VK_RETURN,' TAB=',VK_TAB,' DOWN=',VK_DOWN,' UP=',VK_UP); if (Key=VK_RETURN) then begin OkButtonClick(Sender); Key:=VK_UNKNOWN; end else if (Key=VK_ESCAPE) then begin CancelButtonClick(Sender); Key:=VK_UNKNOWN; end else if Assigned(OnKey) then begin if Sender=TextToFindComboBox then Component:=fdcText else Component:=fdcReplace; OnKey(Sender, Key, Shift, Component); end; end; procedure TLazFindReplaceDialog.OptionsGroupBoxResize(Sender: TObject); var OptionH: Integer; OptionW: Integer; OptionY: Integer; begin OptionH:=OptionsGroupBox.ClientHeight div 5; OptionW:=OptionsGroupBox.ClientWidth-12; OptionY:=2; with CaseSensitiveCheckBox do begin SetBounds(8,OptionY,OptionW,Height); inc(OptionY,OptionH); end; with WholeWordsOnlyCheckBox do begin SetBounds(8,OptionY,OptionW,Height); inc(OptionY,OptionH); end; with RegularExpressionsCheckBox do begin SetBounds(8,OptionY,OptionW,Height); inc(OptionY,OptionH); end; with MultiLineCheckBox do begin SetBounds(8,OptionY,OptionW,Height); inc(OptionY,OptionH); end; with PromptOnReplaceCheckBox do begin SetBounds(8,OptionY,OptionW,Height); inc(OptionY,OptionH); end; end; procedure TLazFindReplaceDialog.LazFindReplaceDialogResize(Sender: TObject); var x: Integer; y: Integer; ComboX: Integer; OptionsGrpW: Integer; begin x:=4; y:=4; ComboX:=x+90; with TextToFindComboBox do begin SetBounds(ComboX,y,Parent.ClientWidth-ComboX-x,Height); end; with TextToFindLabel do begin SetBounds(x,y+3,ComboX-x,Height); end; inc(y,TextToFindComboBox.Height+1); with ReplaceTextComboBox do begin SetBounds(ComboX,y,Parent.ClientWidth-ComboX-x,Height); end; with ReplaceWithLabel do begin SetBounds(x,y+3,ComboX-x,Height); end; inc(y,ReplaceTextComboBox.Height+1); OptionsGrpW:=(ClientWidth-15) div 2; x:=5; with OptionsGroupBox do begin SetBounds(x,y,OptionsGrpW,160); end; with OriginRadioGroup do begin SetBounds(x+OptionsGrpW+x,OptionsGroupBox.Top,OptionsGrpW,65); end; with ScopeRadioGroup do begin SetBounds(OriginRadioGroup.Left, OriginRadioGroup.Top+OriginRadioGroup.Height+5, OriginRadioGroup.Width,65); end; with DirectionRadioGroup do begin SetBounds(OriginRadioGroup.Left, ScopeRadioGroup.Top+ScopeRadioGroup.Height+5, OriginRadioGroup.Width,65); end; with OkButton do begin SetBounds(Parent.ClientWidth-350,Parent.ClientHeight-32,120,Height); end; with ReplaceAllButton do begin SetBounds(OkButton.Left+OkButton.Width+10,OkButton.Top,120,Height); end; with CancelButton do begin SetBounds(ReplaceAllButton.Left+ReplaceAllButton.Width+10,OkButton.Top, 80,Height); end; 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; ModalResult:=mrCancel; 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 MessageDlg(lisUEErrorInRegularExpression, E.Message,mtError,[mbCancel],0); exit; end; end; if ReplaceTextComboBox.Enabled then begin try RegExpr.Substitute(ReplaceText); except on E: ERegExpr do begin MessageDlg(lisUEErrorInRegularExpression, E.Message,mtError,[mbCancel],0); 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; procedure TLazFindReplaceDialog.SetComponentText(c: TFindDlgComponent; const AValue: string); begin case c of fdcText: FindText:=AValue; else Replacetext:=AValue; end; 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 OriginRadioGroup.ItemIndex:=1 else OriginRadioGroup.ItemIndex:=0; if ssoSelectedOnly in NewOptions then ScopeRadioGroup.ItemIndex:=1 else ScopeRadioGroup.ItemIndex:=0; if ssoBackwards in NewOptions then DirectionRadioGroup.ItemIndex:=1 else DirectionRadioGroup.ItemIndex:=0; ReplaceAllButton.Enabled:=ssoReplace in NewOptions; ReplaceTextComboBox.Enabled:=ReplaceAllButton.Enabled; ReplaceWithLabel.Enabled:=ReplaceAllButton.Enabled; PromptOnReplaceCheckBox.Enabled:=ReplaceAllButton.Enabled; if ssoReplace in NewOptions then begin Caption:=lisMenuReplace; OkButton.Caption:=lisMenuReplace; end else begin Caption:=lisMenuFind ; OkButton.Caption:=lisMenuFind ; end; 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 OriginRadioGroup.ItemIndex=1 then Include(Result,ssoEntireScope); if ScopeRadioGroup.ItemIndex=1 then include(Result,ssoSelectedOnly); if DirectionRadioGroup.ItemIndex=1 then include(Result,ssoBackwards); if ReplaceAllButton.Enabled 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); //writeln('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.