Subversion Repositories fastphp

Rev

Rev 83 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
13 daniel-mar 1
unit FindReplace;
2
 
24 daniel-mar 3
  (*
4
  TSynSearchOption = (ssoMatchCase, ssoWholeWord, ssoBackwards,
5
    ssoEntireScope, ssoSelectedOnly, ssoReplace, ssoReplaceAll, ssoPrompt);
13 daniel-mar 6
 
24 daniel-mar 7
  frDisableMatchCase
8
  Disables (grays) the Match Case check box in a find dialog.
9
  frDisableUpDown
10
  Disables (grays) the Up and Down buttons, which determine the direction of the search.
11
  frDisableWholeWord
12
  Disables (grays) the Match Whole Word check box of find dialog.
13
  frDown = not ssoBackwards
14
  Selects the Down button by default when the dialog opens. If the frDown flags is off, Up is selected when the dialog opens. (By default, frDown is on.)
15
  frFindNext
16
  This flag is turned on when the user clicks the Find Next button and turned off when the dialog closes.
17
  frHideMatchCase
18
  Removes the Match Case check box from the dialog.
19
  frHideWholeWord
20
  Removes the Match Whole Word check box from the dialog.
21
  frHideUpDown
22
  Removes the Up and Down buttons from the dialog.
23
  frMatchCase = ssoMatchCase
24
  This flag is turned on (off) when the user selects (deselects) the Match Case check box. To select the check box by default when the dialog opens, set frMatchCase at design time.
25
  frReplace = ssoReplace
26
  Applies to TReplaceDialog only. This flag is set by the system to indicate that the application should replace the current occurrence (and only the current occurrence) of the FindText string with the ReplaceText string. Not used in search routines.
27
  frReplaceAll = ssoReplaceAll
28
  Applies to TReplaceDialog only. This flag is set by the system to indicate that the application should replace all occurrences of the FindText string with the ReplaceText string.
29
  frShowHelp
30
  Displays a Help button in the dialog.
31
  frWholeWord = ssoWholeWord
32
  This flag is turned on (off) when the user selects (deselects) the Match Whole Word check box. To select the check box by default when the dialog opens, set frWholeWord at design time.
33
  *)
34
 
13 daniel-mar 35
interface
36
 
37
uses
83 daniel-mar 38
  Windows, Messages, SysUtils, Classes, Dialogs, SynEdit, System.UITypes;
13 daniel-mar 39
 
40
type
24 daniel-mar 41
  TSynEditFindReplace = class(TComponent)
13 daniel-mar 42
  private
24 daniel-mar 43
    fEditor: TSynEdit;
44
    fReplaceDialog: TReplaceDialog;
45
    fFindDialog: TFindDialog;
25 daniel-mar 46
    fAutofocus: boolean;
13 daniel-mar 47
  protected
22 daniel-mar 48
    type
49
      TFindDirection = (sdDefault, sdForwards, sdBackwards);
50
 
24 daniel-mar 51
    procedure OnFind(Sender: TObject); virtual;
52
    procedure OnReplace(Sender: TObject); virtual;
13 daniel-mar 53
 
67 daniel-mar 54
    procedure DoFind(dialog: TFindDialog; direction: TFindDirection); overload;
24 daniel-mar 55
    procedure DoReplace(dialog: TReplaceDialog; direction: TFindDirection);
13 daniel-mar 56
 
24 daniel-mar 57
    function GetDirection(dialog: TFindDialog): TFindDirection;
13 daniel-mar 58
 
59
  public
24 daniel-mar 60
    constructor Create(AOwner: TComponent); override;
13 daniel-mar 61
 
22 daniel-mar 62
    property FindDialog: TFindDialog read fFindDialog;
63
    property ReplaceDialog: TReplaceDialog read fReplaceDialog;
13 daniel-mar 64
 
22 daniel-mar 65
    procedure CloseDialogs;
66
 
13 daniel-mar 67
    procedure FindExecute;
68
    procedure ReplaceExecute;
69
 
22 daniel-mar 70
    procedure FindContinue;
71
    procedure FindNext;
72
    procedure FindPrev;
73
 
24 daniel-mar 74
    procedure GoToLine(LineNumber: integer);
13 daniel-mar 75
 
76
  published
22 daniel-mar 77
    property Editor: TSynEdit read fEditor write fEditor;
25 daniel-mar 78
    property Autofocus: boolean read fAutofocus write fAutofocus;
13 daniel-mar 79
  end;
80
 
81
implementation
82
 
24 daniel-mar 83
uses
84
  SynEditTypes;
13 daniel-mar 85
 
24 daniel-mar 86
constructor TSynEditFindReplace.Create(AOwner: TComponent);
13 daniel-mar 87
begin
24 daniel-mar 88
  inherited Create(AOwner);
13 daniel-mar 89
 
24 daniel-mar 90
  fFindDialog := TFindDialog.Create(Self);
13 daniel-mar 91
  fFindDialog.OnFind := OnFind;
92
 
24 daniel-mar 93
  fReplaceDialog := TReplaceDialog.Create(Self);
13 daniel-mar 94
  fReplaceDialog.OnReplace := OnReplace;
95
  fReplaceDialog.OnFind := OnFind;
23 daniel-mar 96
  fReplaceDialog.Options := fReplaceDialog.Options + [frHideWholeWord]; // TODO: currently not supported (see below)
13 daniel-mar 97
end;
98
 
24 daniel-mar 99
function TSynEditFindReplace.GetDirection(dialog: TFindDialog): TFindDirection;
13 daniel-mar 100
begin
24 daniel-mar 101
  if frDown in dialog.Options then
102
    result := sdForwards
103
  else
104
    result := sdBackwards;
13 daniel-mar 105
end;
106
 
24 daniel-mar 107
procedure TSynEditFindReplace.DoFind(dialog: TFindDialog; direction: TFindDirection);
13 daniel-mar 108
var
24 daniel-mar 109
  opt: TSynSearchOptions;
110
  found: boolean;
13 daniel-mar 111
begin
24 daniel-mar 112
  if direction = sdDefault then direction := GetDirection(dialog);
13 daniel-mar 113
 
24 daniel-mar 114
  if fEditor.SelAvail then
22 daniel-mar 115
  begin
24 daniel-mar 116
    if direction = sdForwards then
117
    begin
118
      fEditor.SelStart := fEditor.SelStart + 1;
119
      fEditor.SelLength := 0;
120
    end
22 daniel-mar 121
    else
24 daniel-mar 122
    begin
123
      // Links von Selektion springen
124
      fEditor.SelLength := 0;
125
    end;
22 daniel-mar 126
  end;
13 daniel-mar 127
 
24 daniel-mar 128
  opt := [];
129
  if frMatchCase in dialog.Options then Include(opt, ssoMatchCase);
130
  if frWholeWord in dialog.Options then Include(opt, ssoWholeWord);
131
  //if frReplace in dialog.Options then Include(opt, ssoReplace);
132
  //if frReplaceAll in dialog.Options then Include(opt, ssoReplaceAll);
133
  if direction = sdBackwards then Include(opt, ssoBackwards);
134
  //Include(opt, ssoPrompt); // TODO: test. geht nicht?
135
  //if fEditor.SelAvail then Include(opt, ssoSelectedOnly);  // TODO: geht nicht, weil er bei einer suche ja dann etwas selektirert und dann nicht weitergeht
136
  Exclude(opt, ssoEntireScope); // TODO: ok?
13 daniel-mar 137
 
24 daniel-mar 138
  found := fEditor.SearchReplace(dialog.FindText, '', opt) > 0;
13 daniel-mar 139
 
24 daniel-mar 140
  if not found then
13 daniel-mar 141
  begin
67 daniel-mar 142
    // TODO: If single replace was chosen, behave like Notepad and select the last replaced word
24 daniel-mar 143
    if direction = sdForwards then
49 daniel-mar 144
      MessageDlg('End of document reached.', mtInformation, [mbOk], 0)
22 daniel-mar 145
    else
49 daniel-mar 146
      MessageDlg('Begin of document reached.', mtInformation, [mbOk], 0);
13 daniel-mar 147
  end;
25 daniel-mar 148
 
149
  if fAutofocus and fEditor.CanFocus then fEditor.SetFocus;
22 daniel-mar 150
end;
13 daniel-mar 151
 
24 daniel-mar 152
procedure TSynEditFindReplace.DoReplace(dialog: TReplaceDialog; direction: TFindDirection);
22 daniel-mar 153
var
24 daniel-mar 154
  opt: TSynSearchOptions;
155
  numReplacements: integer;
88 daniel-mar 156
  bakSelLenght: Integer;
157
  stmp: string;
158
  bakSelStart: Integer;
22 daniel-mar 159
begin
67 daniel-mar 160
  try
161
    if direction = sdDefault then direction := GetDirection(dialog);
13 daniel-mar 162
 
67 daniel-mar 163
    opt := [];
164
    if frMatchCase in dialog.Options then Include(opt, ssoMatchCase);
165
    if frWholeWord in dialog.Options then Include(opt, ssoWholeWord);
166
    if frReplace in dialog.Options then Include(opt, ssoReplace);
167
    if frReplaceAll in dialog.Options then Include(opt, ssoReplaceAll);
168
    if direction = sdBackwards then Include(opt, ssoBackwards);
169
    Include(opt, ssoPrompt); // TODO: test. geht nicht?
170
    if fEditor.SelAvail then Include(opt, ssoSelectedOnly);
171
    Exclude(opt, ssoEntireScope); // TODO: ok?
13 daniel-mar 172
 
67 daniel-mar 173
    if not (ssoReplaceAll in opt) then
174
    begin
175
      if fEditor.SelLength = 0 then
176
      begin
177
        DoFind(dialog, sdForwards);
178
        exit;
179
      end;
180
    end;
181
 
182
    fEditor.BeginUpdate; // TODO: geht nicht?
183
    //fEditor.BeginUndoBlock;
184
    try
88 daniel-mar 185
      bakSelLenght := 0; // avoid compiler warning
186
      if ssoReplaceAll in opt then
187
      begin
188
        // Remember the selection length
189
        bakSelLenght := fEditor.SelLength;
190
        //bakSelStart := fEditor.SelStart;
191
 
192
        // Remember the selection start (we don't backup fEditor.SelStart, since the replacement might change the location)!
193
        // We assume that character #1 will not be in a text file!
194
        stmp := fEditor.Text;
195
        Insert(chr(1), stmp, fEditor.SelStart+1);
196
        //showmessage(inttostr(ord(stmp[fEditor.SelStart-1])));
197
        //showmessage(inttostr(ord(stmp[fEditor.SelStart])));
198
        //showmessage(inttostr(ord(stmp[fEditor.SelStart+1])));
199
        fEditor.Text := stmp;
200
 
201
        // When the user presses "Replace all", then we want to replace from the very beginning!
202
        fEditor.SelStart := 0;
203
        fEditor.SelLength := 0;
204
      end;
205
 
67 daniel-mar 206
      numReplacements := fEditor.SearchReplace(dialog.FindText, dialog.ReplaceText, opt);
88 daniel-mar 207
 
208
      if ssoReplaceAll in opt then
209
      begin
210
        stmp := fEditor.Text;
211
        bakSelStart := AnsiPos(chr(1), stmp)-1;
212
        Delete(stmp, bakSelStart+1, 1);
213
        fEditor.Text := stmp;
214
 
215
        fEditor.SelStart := bakSelStart;
216
        fEditor.SelLength := bakSelLenght;
217
 
218
        // TODO: The SelStart and SelLength were kept, but the scrollposition did not. What can we do?
219
      end;
67 daniel-mar 220
    finally
221
      //fEditor.EndUndoBlock;
222
      fEditor.EndUpdate;
223
    end;
224
 
225
    if not (ssoReplaceAll in opt) then
226
    begin
227
      DoFind(dialog, sdForwards);
228
    end
229
    else
230
    begin
231
      ShowMessageFmt('%d replaced.', [numReplacements]);
232
    end;
23 daniel-mar 233
  finally
67 daniel-mar 234
    if fAutofocus and fEditor.CanFocus then fEditor.SetFocus;
13 daniel-mar 235
  end;
236
end;
237
 
24 daniel-mar 238
procedure TSynEditFindReplace.OnFind(Sender: TObject);
239
begin
240
  DoFind(Sender as TFindDialog, sdDefault);
241
end;
13 daniel-mar 242
 
24 daniel-mar 243
procedure TSynEditFindReplace.OnReplace(Sender: TObject);
13 daniel-mar 244
begin
24 daniel-mar 245
  DoReplace(Sender as TReplaceDialog, sdDefault);
13 daniel-mar 246
end;
247
 
24 daniel-mar 248
procedure TSynEditFindReplace.FindExecute;
13 daniel-mar 249
begin
250
  fFindDialog.Execute;
251
end;
252
 
24 daniel-mar 253
procedure TSynEditFindReplace.ReplaceExecute;
13 daniel-mar 254
begin
255
  fReplaceDialog.Execute;
256
end;
257
 
24 daniel-mar 258
procedure TSynEditFindReplace.FindContinue;
13 daniel-mar 259
begin
22 daniel-mar 260
  if fFindDialog.FindText = '' then
261
  begin
262
    fFindDialog.Options := fFindDialog.Options + [frDown]; // Default direction: down
263
    FindExecute;
264
  end
265
  else
266
    DoFind(fFindDialog, sdDefault);
267
end;
268
 
24 daniel-mar 269
procedure TSynEditFindReplace.FindNext;
13 daniel-mar 270
begin
22 daniel-mar 271
  if fFindDialog.FindText = '' then
272
  begin
273
    fFindDialog.Options := fFindDialog.Options + [frDown];
274
    FindExecute;
275
  end
276
  else
277
    DoFind(fFindDialog, sdForwards);
13 daniel-mar 278
end;
279
 
24 daniel-mar 280
procedure TSynEditFindReplace.FindPrev;
13 daniel-mar 281
begin
282
  if fFindDialog.FindText = '' then
283
  begin
22 daniel-mar 284
    fFindDialog.Options := fFindDialog.Options - [frDown];
285
    FindExecute;
286
  end
287
  else
288
    DoFind(fFindDialog, sdBackwards);
13 daniel-mar 289
end;
290
 
24 daniel-mar 291
procedure TSynEditFindReplace.GoToLine(LineNumber: integer);
13 daniel-mar 292
var
293
  currentLine: integer;
294
  i: integer;
295
begin
296
  currentLine := 1;
297
 
298
  for i := 1 to fEditor.GetTextLen do
299
  begin
300
    if currentLine = LineNumber then
301
    begin
302
      fEditor.selStart := i;
303
      fEditor.SetFocus;
304
      Exit;
305
    end
306
    else if fEditor.Text = #$D then
24 daniel-mar 307
      inc(currentLine);
13 daniel-mar 308
  end;
309
end;
310
 
24 daniel-mar 311
procedure TSynEditFindReplace.CloseDialogs;
22 daniel-mar 312
begin
313
  fFindDialog.CloseDialog;
314
  fReplaceDialog.CloseDialog;
315
end;
316
 
13 daniel-mar 317
end.