Implement a home brown find + replace dialog, supporting regular expressions, and using all stuff SynEdit gives us. Add main menu item "Search" next to "Edit". Fixes issue #1069.

This commit is contained in:
Ansgar Becker
2010-02-01 23:55:44 +00:00
parent ec6f467a18
commit 02d29ea976
8 changed files with 481 additions and 109 deletions

View File

@ -35,7 +35,8 @@ uses
table_editor in '..\..\source\table_editor.pas' {frmTableEditor},
mysql_api in '..\..\source\mysql_api.pas',
mysql_connection in '..\..\source\mysql_connection.pas',
trigger_editor in '..\..\source\trigger_editor.pas' {frmTriggerEditor: TFrame};
trigger_editor in '..\..\source\trigger_editor.pas' {frmTriggerEditor: TFrame},
searchreplace in '..\..\source\searchreplace.pas' {frmSearchReplace};
{$R ..\..\res\icon.RES}
{$R ..\..\res\version.RES}

View File

@ -195,6 +195,9 @@
<Form>frmTriggerEditor</Form>
<DesignClass>TFrame</DesignClass>
</DCCReference>
<DCCReference Include="..\..\source\searchreplace.pas">
<Form>frmSearchReplace</Form>
</DCCReference>
<RcCompile Include="..\..\res\icon.rc">
<Form>icon.res</Form>
</RcCompile>

BIN
res/icons/find_again.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

View File

@ -190,6 +190,8 @@ const
DEFAULT_FILTERACTIVE = False;
REGNAME_MULTI_INSTANCES = 'AllowMultipleInstances';
DEFAULT_MULTI_INSTANCES = True;
REGNAME_SEARCHTEXT = 'FindDialogSearchHistory';
REGNAME_REPLACETEXT = 'FindDialogReplaceHistory';
REGNAME_FIELDCOLOR_NUMERIC = 'FieldColor_Numeric';
REGNAME_FIELDCOLOR_TEXT = 'FieldColor_Text';

View File

@ -1263,6 +1263,7 @@ object MainForm: TMainForm
Top = 0
Width = 411
Height = 96
OnSearchNotFound = SynMemoQuerySearchNotFound
SingleLineMode = False
Align = alClient
ActiveLineColor = clWindow
@ -1292,6 +1293,7 @@ object MainForm: TMainForm
TabWidth = 3
WantTabs = True
OnDropFiles = SynMemoQueryDropFiles
OnReplaceText = SynMemoQueryReplaceText
OnStatusChange = SynMemoQueryStatusChange
RemovedKeystrokes = <
item
@ -1498,6 +1500,18 @@ object MainForm: TMainForm
Action = actReformatSQL
end
end
object Search1: TMenuItem
Caption = 'Search'
object Findtext1: TMenuItem
Action = actQueryFind
end
object actQueryFindAgain1: TMenuItem
Action = actQueryFindAgain
end
object Replacetext1: TMenuItem
Action = actQueryReplace
end
end
object Extra1: TMenuItem
Tag = 19
Caption = '&Tools'
@ -2146,7 +2160,7 @@ object MainForm: TMainForm
Hint = 'Find text ...'
ImageIndex = 30
ShortCut = 16454
OnExecute = actQueryFindExecute
OnExecute = actQueryFindReplaceExecute
end
object actQueryReplace: TAction
Category = 'SQL'
@ -2155,7 +2169,14 @@ object MainForm: TMainForm
Hint = 'Replace text ...'
ImageIndex = 59
ShortCut = 16466
OnExecute = actQueryReplaceExecute
OnExecute = actQueryFindReplaceExecute
end
object actQueryFindAgain: TAction
Category = 'SQL'
Caption = 'Find or replace again'
ImageIndex = 142
ShortCut = 114
OnExecute = actQueryFindAgainExecute
end
object actSetDelimiter: TAction
Category = 'SQL'
@ -6231,23 +6252,42 @@ object MainForm: TMainForm
43F83D4D2738195C82C5B763381B5E31A3DD0479CBAF6D7E05EBBBC0DC883A01
4C91E3672618257B704396A99BE46D97C9623AF93A3F9224CD92FC8501FA8D75
FE4FCCAC61ACF6D72F62B02F294EFE3B9F0000000049454E44AE426082}
end
item
Background = clWindow
Name = 'find_again'
PngImage.Data = {
89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
610000001974455874536F667477617265005061696E742E4E45542076332E35
2E32D7EE6943000002C24944415478DAAD934B48547114C6BF3BB719535354A6
4143255F93AF468D5132CD3470229D5AD4226813650FA3400B1795B98841DB55
D426B0D08A30717415F9487BA838D6989A9889927847677CA6F3721EF7D99D11
DA45481D38FC17E7FFFDCEE17C1C02FF18C47F07E8EE3F31C5C5ED8EA1280AD5
1565C4A3C636C16E5BC7E8D0E074CB8B7AE55F013774F74C39D9D9314D2F9F43
1410E5D76F0BCA94746C389DE69A6BE7A3FF083877A54A484ECF84C36E436060
10DA9A1A3064E8250A355A2155B50F325900FADF77FADE5796B9D90B36BBD5B5
BEBAC2138DFACEED635F870BC3C2E5ED2429C1EAF212BE8F8F826568F88036EB
1A18991C494989703BD6306EEC455FF79B6A8665EED25EAF403C78DAB24D8C1F
A121C1B1C64F83FE69427725234A11069AE31887C32B655DEB98999D07EB5A83
529988BAEA0A8A63D904F12B47D43D6C900A024F8747C8F16D6C181CC789DD19
B85D1B301A7A119F948CC43DA9605916C16A03ECDE35D49F1D11447180980C71
B1F2A674D13CE75C5EB4C822E40A2C2D98E171BB60B7592D6E97B3542A0BA815
BB95040505437B27123BC36230611E4673F924290278FF12B5274EF71D282CCE
5767ED85D5E604356F41F3B3C7CD795785531058D0BC982C87A8F004A8A2F3F1
79F61DC6CD4674545A083F40A33DC9A6A46792A5A547A16F6D85D7E3C1887160
E26025528BD3CE801378703C071E02166DF3080F56C030D38D91B9FE4D1B8B8E
1C13545939F0015AF47A4824243E76BDA6F3AA24B2125519A89FD360C429589E
01C331625D06454834FA673A370179451A213BF7100A0A0AD0D5D5099224D1DB
D30ED565078E675C12859C3F7D932CD84C88D81129761FC0DBC98E493F409D5B
20F836CF88DED3B4D7EF82E831726E11BCDBE395D03C0D0F4B2351918EFDF11A
0C9906D033D53546E990B1A5634AD3112BEAD8C3F20F533D5FA85AA8B77C8D71
3558E178526EAAE57EEB7E01DE445494B226E2DE0000000049454E44AE426082}
end>
PngOptions = [pngBlendOnDisabled, pngGrayscaleOnDisabled]
Left = 104
Top = 232
Bitmap = {}
end
object FindDialogQuery: TFindDialog
OnFind = FindDialogQueryFind
Left = 104
Top = 200
end
object ReplaceDialogQuery: TReplaceDialog
OnFind = ReplaceDialogQueryFind
OnReplace = ReplaceDialogQueryReplace
Left = 136
Top = 200
end
object PopupQueryLoad: TPopupMenu
Left = 104
Top = 64
@ -6968,4 +7008,8 @@ object MainForm: TMainForm
Left = 40
Top = 269
end
object SynEditRegexSearch1: TSynEditRegexSearch
Left = 104
Top = 304
end
end

View File

@ -13,12 +13,12 @@ uses
Windows, SysUtils, Classes, Graphics, GraphUtil, Forms, Controls, Menus, StdCtrls, Dialogs, Buttons,
Messages, ExtCtrls, ComCtrls, StdActns, ActnList, ImgList, ToolWin, Clipbrd, SynMemo,
SynEdit, SynEditTypes, SynEditKeyCmds, VirtualTrees, DateUtils, PngImageList,
ShlObj, SynEditMiscClasses, SynEditSearch, SynCompletionProposal, SynEditHighlighter,
ShlObj, SynEditMiscClasses, SynEditSearch, SynEditRegexSearch, SynCompletionProposal, SynEditHighlighter,
SynHighlighterSQL, Tabs, SynUnicode, SynRegExpr, WideStrUtils, ExtActns,
CommCtrl, Contnrs, PngSpeedButton, Generics.Collections,
routine_editor, trigger_editor, options, EditVar, helpers, createdatabase, table_editor,
TableTools, View, Usermanager, SelectDBObject, connections, sqlhelp, mysql_connection,
mysql_api, insertfiles;
mysql_api, insertfiles, searchreplace;
type
@ -182,8 +182,6 @@ type
actQueryWordWrap: TAction;
actQueryFind: TAction;
actQueryReplace: TAction;
FindDialogQuery: TFindDialog;
ReplaceDialogQuery: TReplaceDialog;
ToolBarQuery: TToolBar;
btnExecuteQuery: TToolButton;
btnLoadSQL: TToolButton;
@ -300,6 +298,7 @@ type
OpenDialogSQLFile: TOpenDialog;
SaveDialogSQLFile: TSaveDialog;
SynEditSearch1: TSynEditSearch;
SynEditRegexSearch1: TSynEditRegexSearch;
tabCommandStats: TTabSheet;
ListCommandStats: TVirtualStringTree;
QF13: TMenuItem;
@ -454,6 +453,11 @@ type
menuFilterInsertFunction: TMenuItem;
actBlobAsText: TAction;
btnBlobAsText: TToolButton;
actQueryFindAgain: TAction;
Search1: TMenuItem;
Findtext1: TMenuItem;
actQueryFindAgain1: TMenuItem;
Replacetext1: TMenuItem;
procedure refreshMonitorConfig;
procedure loadWindowConfig;
procedure saveWindowConfig;
@ -504,8 +508,7 @@ type
procedure actNewWindowExecute(Sender: TObject);
procedure actSessionManagerExecute(Sender: TObject);
procedure actPreferencesExecute(Sender: TObject);
procedure actQueryFindExecute(Sender: TObject);
procedure actQueryReplaceExecute(Sender: TObject);
procedure actQueryFindReplaceExecute(Sender: TObject);
procedure actQueryStopOnErrorsExecute(Sender: TObject);
procedure actQueryWordWrapExecute(Sender: TObject);
procedure actReadmeExecute(Sender: TObject);
@ -518,9 +521,6 @@ type
procedure actSQLhelpExecute(Sender: TObject);
procedure actUpdateCheckExecute(Sender: TObject);
procedure actWebbrowse(Sender: TObject);
procedure FindDialogQueryFind(Sender: TObject);
procedure ReplaceDialogQueryFind(Sender: TObject);
procedure ReplaceDialogQueryReplace(Sender: TObject);
procedure actCopyAsSQLExecute(Sender: TObject);
procedure actSelectTreeBackgroundExecute(Sender: TObject);
procedure popupQueryPopup(Sender: TObject);
@ -722,6 +722,10 @@ type
NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
var Allowed: Boolean);
procedure actBlobAsTextExecute(Sender: TObject);
procedure SynMemoQuerySearchNotFound(Sender: TObject; FindText: string);
procedure SynMemoQueryReplaceText(Sender: TObject; const ASearch,
AReplace: string; Line, Column: Integer; var Action: TSynReplaceAction);
procedure actQueryFindAgainExecute(Sender: TObject);
private
ReachedEOT: Boolean;
FDelimiter: String;
@ -742,6 +746,7 @@ type
FCmdlineFilenames: TStringlist;
FCmdlineConnectionParams: TConnectionParameters;
FCmdlineSessionName: String;
FSearchReplaceExecuted: Boolean;
procedure ParseCommandLineParameters(Parameters: TStringlist);
procedure SetDelimiter(Value: String);
procedure DisplayRowCountStats(MatchingRows: Int64 = -1);
@ -782,6 +787,7 @@ type
TableEditor: TfrmTableEditor;
InsertFiles: TfrmInsertFiles;
EditVariableForm: TfrmEditVariable;
SearchReplaceDialog: TfrmSearchReplace;
// Virtual Tree data arrays
VTRowDataListVariables,
@ -1091,6 +1097,7 @@ begin
FreeAndNil(TableEditor);
FreeAndNil(TriggerEditor);
FreeAndNil(CreateDatabaseForm);
FreeAndNil(SearchReplaceDialog);
// Close database connection
DoDisconnect;
@ -2553,25 +2560,78 @@ begin
end;
procedure TMainForm.actQueryFindExecute(Sender: TObject);
procedure TMainForm.actQueryFindReplaceExecute(Sender: TObject);
var
DlgResult: TModalResult;
Occurences: Integer;
begin
// if something is selected search for that text
if ActiveQueryMemo.SelAvail then
FindDialogQuery.FindText := ActiveQueryMemo.SelText
// Display search + replace dialog
if not Assigned(SearchReplaceDialog) then
SearchReplaceDialog := TfrmSearchReplace.Create(Self);
SearchReplaceDialog.Editor := ActiveQueryMemo;
SearchReplaceDialog.chkReplace.Checked := Sender = actQueryReplace;
DlgResult := SearchReplaceDialog.ShowModal;
if SearchReplaceDialog.chkRegularExpression.Checked then
SearchReplaceDialog.Editor.SearchEngine := SynEditRegexSearch1
else
FindDialogQuery.FindText := ActiveQueryMemo.WordAtCursor;
FindDialogQuery.Execute;
SearchReplaceDialog.Editor.SearchEngine := SynEditSearch1;
ShowStatus('Searching ...');
case DlgResult of
mrOK, mrAll: begin
Occurences := SearchReplaceDialog.Editor.SearchReplace(
SearchReplaceDialog.comboSearch.Text,
SearchReplaceDialog.comboReplace.Text,
SearchReplaceDialog.Options
);
FSearchReplaceExecuted := True; // Helper for later F3 hits
if DlgResult = mrAll then
ShowStatus('Text "'+SearchReplaceDialog.comboSearch.Text+'" '+FormatNumber(Occurences)+' times replaced.', 0);
end;
mrCancel: Exit;
end;
ShowStatus(STATUS_MSG_READY);
end;
procedure TMainForm.actQueryReplaceExecute(Sender: TObject);
procedure TMainForm.actQueryFindAgainExecute(Sender: TObject);
begin
// if something is selected search for that text
if ActiveQueryMemo.SelAvail then
ReplaceDialogQuery.FindText := ActiveQueryMemo.SelText
// F3 - search or replace again, using previous settings
if not FSearchReplaceExecuted then begin
// Display dialog if not done yet
actQueryFindReplaceExecute(Sender)
end else begin
SearchReplaceDialog.Editor := ActiveQueryMemo;
if SearchReplaceDialog.chkRegularExpression.Checked then
SearchReplaceDialog.Editor.SearchEngine := SynEditRegexSearch1
else
ReplaceDialogQuery.FindText := ActiveQueryMemo.WordAtCursor;
ReplaceDialogQuery.Execute;
SearchReplaceDialog.Editor.SearchEngine := SynEditSearch1;
SearchReplaceDialog.Editor.SearchReplace(
SearchReplaceDialog.comboSearch.Text,
SearchReplaceDialog.comboReplace.Text,
SearchReplaceDialog.Options
);
end;
end;
procedure TMainForm.SynMemoQuerySearchNotFound(Sender: TObject; FindText: string);
begin
// No text found
ShowStatus('Text "'+sstr(FindText,50)+'" not found.', 0);
MessageBeep(MB_ICONASTERISK);
end;
procedure TMainForm.SynMemoQueryReplaceText(Sender: TObject; const ASearch,
AReplace: string; Line, Column: Integer; var Action: TSynReplaceAction);
begin
// Fires when "Replace all" in search dialog was pressed with activated "Prompt on replace"
case MessageDlg('Replace this occurrence of "'+sstr(ASearch, 100)+'"?', mtConfirmation, [mbYes, mbYesToAll, mbNo, mbCancel], 0) of
mrYes: Action := raReplace;
mrYesToAll: Action := raReplaceAll;
mrNo: Action := raSkip;
mrCancel: Action := raCancel;
end;
end;
@ -2792,68 +2852,6 @@ begin
end;
procedure TMainForm.FindDialogQueryFind(Sender: TObject);
var
Options: TSynSearchOptions;
Search: String;
begin
Search := FindDialogQuery.FindText;
Options := [];
if Sender is TReplaceDialog then
Include(Options, ssoEntireScope);
if not (frDown in FindDialogQuery.Options) then
Include(Options, ssoBackwards);
if frMatchCase in FindDialogQuery.Options then
Include(Options, ssoMatchCase);
if frWholeWord in FindDialogQuery.Options then
Include(Options, ssoWholeWord);
if ActiveQueryMemo.SearchReplace(Search, '', Options) = 0 then
begin
MessageBeep(MB_ICONASTERISK);
ShowStatus( 'SearchText ''' + Search + ''' not found!', 0);
end;
end;
procedure TMainForm.ReplaceDialogQueryFind(Sender: TObject);
begin
FindDialogQuery.FindText := ReplaceDialogQuery.FindText;
FindDialogQueryFind( ReplaceDialogQuery );
end;
procedure TMainForm.ReplaceDialogQueryReplace(Sender: TObject);
var
Options: TSynSearchOptions;
Search: String;
begin
Search := ReplaceDialogQuery.FindText;
Options := [ssoEntireScope]; // Do replaces always on entire scope, because the standard-dialog lacks of a down/up-option
if frReplaceAll in ReplaceDialogQuery.Options then
Include( Options, ssoReplaceAll );
if not (frDown in ReplaceDialogQuery.Options) then
Include(Options, ssoBackwards);
if frMatchCase in ReplaceDialogQuery.Options then
Include(Options, ssoMatchCase);
if frWholeWord in ReplaceDialogQuery.Options then
Include(Options, ssoWholeWord);
if frReplace in ReplaceDialogQuery.Options then // Replace instead of ReplaceAll is pressed
Include(Options, ssoReplace)
else
Include(Options, ssoReplaceAll);
if ActiveQueryMemo.SearchReplace( Search, ReplaceDialogQuery.ReplaceText, Options) = 0 then
begin
MessageBeep(MB_ICONASTERISK);
ShowStatus( 'SearchText ''' + Search + ''' not found!', 0);
if ssoBackwards in Options then
ActiveQueryMemo.BlockEnd := ActiveQueryMemo.BlockBegin
else
ActiveQueryMemo.BlockBegin := ActiveQueryMemo.BlockEnd;
ActiveQueryMemo.CaretXY := ActiveQueryMemo.BlockBegin;
end;
end;
procedure TMainform.FillPopupQueryLoad;
var
i, j: Integer;
@ -3774,8 +3772,9 @@ begin
actSaveSQLselection.Enabled := InQueryTab and HasSelection;
actSaveSQLSnippet.Enabled := InQueryTab and NotEmpty;
actSaveSQLSelectionSnippet.Enabled := InQueryTab and HasSelection;
actQueryFind.Enabled := InQueryTab and NotEmpty;
actQueryReplace.Enabled := InQueryTab and NotEmpty;
actQueryFind.Enabled := InQueryTab;
actQueryReplace.Enabled := InQueryTab;
actQueryFindAgain.Enabled := InQueryTab;
// We need a pressed button which somehow does not work in conjunction with Enabled=False
// actQueryStopOnErrors.Enabled := QueryTabActive;
actQueryWordWrap.Enabled := InQueryTab;
@ -7873,8 +7872,6 @@ begin
// Copy text from a focused control to clipboard
Success := False;
Control := Screen.ActiveControl;
// Do not handle Search/replace dialog
if not Control.Focused then Exit;
DoCut := Sender = actCut;
if Control is TCustomEdit then begin
Edit := TCustomEdit(Control);
@ -7925,8 +7922,6 @@ begin
// Paste text into the focused control
Success := False;
Control := Screen.ActiveControl;
// Do not handle Search/replace dialog
if not Control.Focused then Exit;
if not Clipboard.HasFormat(CF_TEXT) then begin
// Do nothing, we cannot paste a picture or so
end else if Control is TCustomEdit then begin
@ -7969,8 +7964,6 @@ begin
// Select all items, text or whatever
Success := False;
Control := Screen.ActiveControl;
// Do not handle Search/replace dialog
if not Control.Focused then Exit;
if Control is TCustomEdit then begin
TCustomEdit(Control).SelectAll;
Success := True;
@ -8006,8 +7999,6 @@ begin
// Invert selection in grids or listboxes
Success := False;
Control := Screen.ActiveControl;
// Do not handle Search/replace dialog
if not Control.Focused then Exit;
if Control is TVirtualStringTree then begin
Grid := TVirtualStringTree(Control);
if toMultiSelect in Grid.TreeOptions.SelectionOptions then begin
@ -8316,13 +8307,14 @@ begin
QueryTab.Memo.RightEdge := SynMemoQuery.RightEdge;
QueryTab.Memo.WantTabs := SynMemoQuery.WantTabs;
QueryTab.Memo.Highlighter := SynMemoQuery.Highlighter;
QueryTab.Memo.SearchEngine := SynMemoQuery.SearchEngine;
QueryTab.Memo.Gutter.Assign(SynMemoQuery.Gutter);
QueryTab.Memo.Font.Assign(SynMemoQuery.Font);
QueryTab.Memo.ActiveLineColor := SynMemoQuery.ActiveLineColor;
QueryTab.Memo.OnDragDrop := SynMemoQuery.OnDragDrop;
QueryTab.Memo.OnDragOver := SynMemoQuery.OnDragOver;
QueryTab.Memo.OnDropFiles := SynMemoQuery.OnDropFiles;
QueryTab.Memo.OnSearchNotFound := SynMemoQuery.OnSearchNotFound;
QueryTab.Memo.OnReplaceText := SynMemoQuery.OnReplaceText;
QueryTab.Memo.OnStatusChange := SynMemoQuery.OnStatusChange;
SynCompletionProposal.AddEditor(QueryTab.Memo);

180
source/searchreplace.dfm Normal file
View File

@ -0,0 +1,180 @@
object frmSearchReplace: TfrmSearchReplace
Left = 0
Top = 0
BorderStyle = bsDialog
Caption = 'Search and replace text'
ClientHeight = 236
ClientWidth = 388
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poOwnerFormCenter
OnClose = FormClose
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
DesignSize = (
388
236)
PixelsPerInch = 96
TextHeight = 13
object lblSearch: TLabel
Left = 8
Top = 11
Width = 60
Height = 13
Caption = '&Text to find:'
FocusControl = comboSearch
end
object btnCancel: TButton
Left = 305
Top = 203
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Cancel = True
Caption = 'Cancel'
ModalResult = 2
TabOrder = 9
end
object btnReplaceAll: TButton
Left = 224
Top = 203
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Replace all'
ModalResult = 8
TabOrder = 8
end
object chkReplace: TCheckBox
Left = 8
Top = 35
Width = 73
Height = 17
Caption = 'Replace:'
TabOrder = 1
OnClick = chkReplaceClick
end
object comboSearch: TComboBox
Left = 90
Top = 8
Width = 290
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 0
Text = 'comboSearch'
TextHint = 'Enter text to find ...'
OnExit = comboSearchReplaceExit
end
object comboReplace: TComboBox
Left = 90
Top = 33
Width = 290
Height = 21
Anchors = [akLeft, akTop, akRight]
Enabled = False
TabOrder = 2
Text = 'comboReplace'
TextHint = 'Enter replacement pattern ...'
OnExit = comboSearchReplaceExit
end
object grpOptions: TGroupBox
Left = 8
Top = 60
Width = 372
Height = 69
Anchors = [akLeft, akTop, akRight]
Caption = 'Options'
TabOrder = 3
object chkCaseSensitive: TCheckBox
Left = 12
Top = 18
Width = 130
Height = 17
Caption = 'Case sensitive'
TabOrder = 0
end
object chkWholeWords: TCheckBox
Left = 12
Top = 41
Width = 130
Height = 17
Caption = 'Whole words'
TabOrder = 1
end
object chkRegularExpression: TCheckBox
Left = 156
Top = 18
Width = 130
Height = 17
Caption = 'Regular expression'
ParentShowHint = False
ShowHint = True
TabOrder = 2
OnClick = ValidateControls
end
object chkPromptOnReplace: TCheckBox
Left = 156
Top = 41
Width = 130
Height = 17
Caption = 'Prompt on replace'
TabOrder = 3
end
end
object grpDirection: TRadioGroup
Left = 8
Top = 135
Width = 120
Height = 62
Anchors = [akLeft, akTop, akBottom]
Caption = 'Direction'
ItemIndex = 0
Items.Strings = (
'&Forward'
'&Backward')
TabOrder = 4
end
object grpOrigin: TRadioGroup
Left = 134
Top = 135
Width = 120
Height = 62
Anchors = [akLeft, akTop, akBottom]
Caption = 'Origin'
ItemIndex = 0
Items.Strings = (
'From cursor'
'Entire scope')
TabOrder = 5
end
object grpScope: TRadioGroup
Left = 260
Top = 135
Width = 120
Height = 62
Anchors = [akLeft, akTop, akRight, akBottom]
Caption = 'Scope'
ItemIndex = 0
Items.Strings = (
'Global'
'Selected text')
TabOrder = 6
end
object btnOK: TButton
Left = 143
Top = 203
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'OK'
Default = True
ModalResult = 1
TabOrder = 7
end
end

150
source/searchreplace.pas Normal file
View File

@ -0,0 +1,150 @@
unit searchreplace;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls,
ExtCtrls, SynMemo, SynEditSearch, SynEditTypes, SynEditMiscClasses;
type
TfrmSearchReplace = class(TForm)
btnCancel: TButton;
btnReplaceAll: TButton;
lblSearch: TLabel;
chkReplace: TCheckBox;
comboSearch: TComboBox;
comboReplace: TComboBox;
grpOptions: TGroupBox;
chkCaseSensitive: TCheckBox;
chkWholeWords: TCheckBox;
chkRegularExpression: TCheckBox;
chkPromptOnReplace: TCheckBox;
grpDirection: TRadioGroup;
grpOrigin: TRadioGroup;
grpScope: TRadioGroup;
btnOK: TButton;
procedure ValidateControls(Sender: TObject);
procedure chkReplaceClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure comboSearchReplaceExit(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Editor: TSynMemo;
Options: TSynSearchOptions;
end;
implementation
{$R *.dfm}
uses main, helpers;
procedure TfrmSearchReplace.FormCreate(Sender: TObject);
begin
comboSearch.Items.Text := GetRegValue(REGNAME_SEARCHTEXT, '');
comboReplace.Items.Text := GetRegValue(REGNAME_REPLACETEXT, '');
comboSearch.Text := '';
comboReplace.Text := '';
if comboSearch.Items.Count > 0 then comboSearch.Text := comboSearch.Items[0];
if comboReplace.Items.Count > 0 then comboReplace.Text := comboReplace.Items[0];
chkRegularExpression.Hint := 'Search patterns:'+CRLF+
' ^ Start of line'+CRLF+
' $ End of line'+CRLF+
' \w Any word character'+CRLF+
' \d Digit (0-9)'+CRLF+
' \s Whitespace'+CRLF+
'Replacement patterns:'+CRLF+
' $0 .. $n Callback parentheses'
;
end;
procedure TfrmSearchReplace.FormShow(Sender: TObject);
var
SearchText: String;
begin
// Prefill search editor with selected text
if Editor.SelAvail then
SearchText := Editor.SelText
else
SearchText := Editor.WordAtCursor;
if SearchText <> '' then
comboSearch.Text := SearchText;
ValidateControls(Sender);
comboSearch.SetFocus;
end;
procedure TfrmSearchReplace.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// Set SynEditSearch options, which the caller can use in Editor.SearchReplace()
Options := [];
if chkReplace.Checked then Include(Options, ssoReplace);
if chkCaseSensitive.Checked then Include(Options, ssoMatchCase);
if chkWholeWords.Checked then Include(Options, ssoWholeWord);
if chkPromptOnReplace.Checked and chkPromptOnReplace.Enabled then Include(Options, ssoPrompt);
if grpDirection.ItemIndex = 1 then Include(Options, ssoBackwards);
if grpOrigin.ItemIndex = 1 then Include(Options, ssoEntireScope);
if grpScope.ItemIndex = 1 then Include(Options, ssoSelectedOnly);
if ModalResult = mrAll then Include(Options, ssoReplaceAll);
end;
procedure TfrmSearchReplace.FormDestroy(Sender: TObject);
begin
OpenRegistry;
Mainreg.WriteString(REGNAME_SEARCHTEXT, comboSearch.Items.Text);
Mainreg.WriteString(REGNAME_REPLACETEXT, comboReplace.Items.Text);
end;
procedure TfrmSearchReplace.chkReplaceClick(Sender: TObject);
begin
// Jump to replace editor
ValidateControls(Sender);
if comboReplace.Enabled then
ActiveControl := comboReplace;
end;
procedure TfrmSearchReplace.ValidateControls(Sender: TObject);
begin
// Enable or disable various controls
comboReplace.Enabled := chkReplace.Checked;
chkPromptOnReplace.Enabled := chkReplace.Checked;
btnReplaceAll.Enabled := chkReplace.Checked;
if chkReplace.Checked then
btnOK.Caption := 'Replace'
else
btnOK.Caption := 'Find';
end;
procedure TfrmSearchReplace.comboSearchReplaceExit(Sender: TObject);
var
Combo: TComboBox;
idx: Integer;
begin
// Store search or replace text history
Combo := Sender as TComboBox;
if Combo.Text = '' then
Exit;
idx := Combo.Items.IndexOf(Combo.Text);
if idx > -1 then
Combo.Items.Move(idx, 0)
else
Combo.Items.Insert(0, Combo.Text);
Combo.Text := Combo.Items[0];
end;
end.