diff --git a/packages/delphi2010/heidisql.dpr b/packages/delphi2010/heidisql.dpr
index ccafc7d7..ea665933 100644
--- a/packages/delphi2010/heidisql.dpr
+++ b/packages/delphi2010/heidisql.dpr
@@ -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}
diff --git a/packages/delphi2010/heidisql.dproj b/packages/delphi2010/heidisql.dproj
index d56bf46b..2f120fac 100644
--- a/packages/delphi2010/heidisql.dproj
+++ b/packages/delphi2010/heidisql.dproj
@@ -195,6 +195,9 @@
TFrame
+
+
+
diff --git a/res/icons/find_again.png b/res/icons/find_again.png
new file mode 100644
index 00000000..272fe6b8
Binary files /dev/null and b/res/icons/find_again.png differ
diff --git a/source/const.inc b/source/const.inc
index b55b023c..95225ac7 100644
--- a/source/const.inc
+++ b/source/const.inc
@@ -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';
diff --git a/source/main.dfm b/source/main.dfm
index 0784aeea..6445b9b9 100644
--- a/source/main.dfm
+++ b/source/main.dfm
@@ -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
diff --git a/source/main.pas b/source/main.pas
index 00759818..09b008c8 100644
--- a/source/main.pas
+++ b/source/main.pas
@@ -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
- else
- ReplaceDialogQuery.FindText := ActiveQueryMemo.WordAtCursor;
- ReplaceDialogQuery.Execute;
+ // 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
+ 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);
diff --git a/source/searchreplace.dfm b/source/searchreplace.dfm
new file mode 100644
index 00000000..65e915ff
--- /dev/null
+++ b/source/searchreplace.dfm
@@ -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
diff --git a/source/searchreplace.pas b/source/searchreplace.pas
new file mode 100644
index 00000000..6c22ee8c
--- /dev/null
+++ b/source/searchreplace.pas
@@ -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.