Rev 23 | Rev 25 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 23 | Rev 24 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | unit FindReplace; |
1 | unit FindReplace; |
2 | 2 | ||
- | 3 | (* |
|
- | 4 | TSynSearchOption = (ssoMatchCase, ssoWholeWord, ssoBackwards, |
|
- | 5 | ssoEntireScope, ssoSelectedOnly, ssoReplace, ssoReplaceAll, ssoPrompt); |
|
- | 6 | ||
3 | // FindReplace.pas |
7 | frDisableMatchCase |
4 | // Source: http://www.tek-tips.com/viewthread.cfm?qid=160357 |
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. |
|
5 | // 18 Nov 2001 "bearsite4" |
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 |
|
6 | // Changes by Daniel Marschall, especially to make it compatible with TSynEdit |
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 | *) |
|
7 | 34 | ||
8 | interface |
35 | interface |
9 | 36 | ||
10 | uses |
37 | uses |
11 | Windows, Messages, SysUtils, Classes, Dialogs, SynEdit; |
38 | Windows, Messages, SysUtils, Classes, Dialogs, SynEdit; |
12 | 39 | ||
13 | type |
40 | type |
14 | FindReplaceCommunication = ( frcAlertUser, frcAlertReplace, frcEndReached ); |
- | |
15 | {allows the replace functions to use the find function avoiding alot |
- | |
16 | of code duplication. frcAlertuser means that when the find function |
- | |
17 | has reached the end of the text while searching for a word it will pop |
- | |
18 | up a message saying the word can't be found. frcAlertReplace |
- | |
19 | tells the find function not to display a message to the user saying that |
- | |
20 | the word can't be found but instead to set the state to frcEndReached to |
- | |
21 | let the replace function know it's reached the end of the text} |
- | |
22 | - | ||
23 | TReplaceFunc = function ( const S1, S2: string ): Integer; |
- | |
24 | - | ||
25 | TFindReplace = class(TComponent) |
41 | TSynEditFindReplace = class(TComponent) |
26 | private |
42 | private |
27 | fEditor: TSynEdit; {the richedit or memo component to hook it up to} |
43 | fEditor: TSynEdit; |
28 | fReplaceDialog: TReplaceDialog; {the replace dialog} |
44 | fReplaceDialog: TReplaceDialog; |
29 | fFindDialog: TFindDialog; {the find dialog} |
45 | fFindDialog: TFindDialog; |
30 | - | ||
31 | FindActionOnEnd: FindReplaceCommunication; {the action the find function |
- | |
32 | should take when it reaches the end of the text while searching for the |
- | |
33 | word} |
- | |
34 | - | ||
35 | function TestWholeWord( Sender: TFindDialog; TestString: string ): boolean; |
- | |
36 | {returns a true or false depending on whether the user chose the whole word |
- | |
37 | only option and whether or not the word is a whole word. Actually, it can |
- | |
38 | test multiple words in a single string as well.} |
- | |
39 | - | ||
40 | protected |
46 | protected |
41 | type |
47 | type |
42 | TFindDirection = (sdDefault, sdForwards, sdBackwards); |
48 | TFindDirection = (sdDefault, sdForwards, sdBackwards); |
43 | 49 | ||
44 | procedure FindForwards( Sender: TFindDialog; start, finish: integer ); |
- | |
45 | {search through the editor in a forwards direction} |
- | |
46 | procedure FindBackwards( Sender: TFindDialog; start, finish: integer ); |
- | |
47 | {search through the editor in a backwards direction} |
- | |
48 | - | ||
49 | {defined event handlers} |
- | |
50 | procedure OnFind( Sender: TObject ); virtual; |
50 | procedure OnFind(Sender: TObject); virtual; |
51 | procedure OnReplace( Sender: TObject ); virtual; |
51 | procedure OnReplace(Sender: TObject); virtual; |
52 | 52 | ||
53 | {the centralised find/replace functions} |
- | |
54 | function TryAndMatch( Sender: TFindDialog; index, finish: integer ): boolean; virtual; |
- | |
55 | function TryAndReplace(dialog: TReplaceDialog): boolean; virtual; |
- | |
56 | - | ||
57 | procedure DoReplace(dialog: TReplaceDialog); virtual; |
- | |
58 | {the replace function that coordinates all the work} |
- | |
59 | procedure DoReplaceAll(dialog: TReplaceDialog); virtual; |
- | |
60 | {the replace all function that coordinates all the work} |
- | |
61 | - | ||
62 | procedure DoFind(dialog: TFindDialog; direction: TFindDirection); |
53 | procedure DoFind(dialog: TFindDialog; direction: TFindDirection); |
- | 54 | procedure DoReplace(dialog: TReplaceDialog; direction: TFindDirection); |
|
- | 55 | ||
- | 56 | function GetDirection(dialog: TFindDialog): TFindDirection; |
|
63 | 57 | ||
64 | public |
58 | public |
65 | constructor Create( AOwner: TComponent); override; |
59 | constructor Create(AOwner: TComponent); override; |
66 | 60 | ||
67 | property FindDialog: TFindDialog read fFindDialog; |
61 | property FindDialog: TFindDialog read fFindDialog; |
68 | property ReplaceDialog: TReplaceDialog read fReplaceDialog; |
62 | property ReplaceDialog: TReplaceDialog read fReplaceDialog; |
69 | 63 | ||
70 | procedure CloseDialogs; |
64 | procedure CloseDialogs; |
71 | 65 | ||
72 | procedure FindExecute; |
66 | procedure FindExecute; |
73 | {opens the find dialog} |
- | |
74 | procedure ReplaceExecute; |
67 | procedure ReplaceExecute; |
75 | {opens the replace dialog} |
- | |
76 | 68 | ||
77 | procedure FindContinue; |
69 | procedure FindContinue; |
78 | procedure FindNext; |
70 | procedure FindNext; |
79 | procedure FindPrev; |
71 | procedure FindPrev; |
80 | 72 | ||
Line 82... | Line 74... | ||
82 | 74 | ||
83 | published |
75 | published |
84 | property Editor: TSynEdit read fEditor write fEditor; |
76 | property Editor: TSynEdit read fEditor write fEditor; |
85 | end; |
77 | end; |
86 | 78 | ||
87 | (* |
- | |
88 | procedure Register; |
- | |
89 | *) |
- | |
90 | - | ||
91 | implementation |
79 | implementation |
92 | 80 | ||
93 | (* |
81 | uses |
94 | {$R findrep.dcr} |
82 | SynEditTypes; |
95 | *) |
- | |
96 | 83 | ||
97 | constructor TFindReplace.Create( AOwner: TComponent ); |
84 | constructor TSynEditFindReplace.Create(AOwner: TComponent); |
98 | begin |
85 | begin |
99 | inherited; |
86 | inherited Create(AOwner); |
100 | 87 | ||
101 | {create the find dialog} |
- | |
102 | fFindDialog := TFindDialog.Create( Self ); |
88 | fFindDialog := TFindDialog.Create(Self); |
103 | {set up the event handlers} |
- | |
104 | fFindDialog.OnFind := OnFind; |
89 | fFindDialog.OnFind := OnFind; |
105 | 90 | ||
106 | {create the replace dialog} |
- | |
107 | fReplaceDialog := TReplaceDialog.Create( Self ); |
91 | fReplaceDialog := TReplaceDialog.Create(Self); |
108 | {set up the event handlers} |
- | |
109 | fReplaceDialog.OnReplace := OnReplace; |
92 | fReplaceDialog.OnReplace := OnReplace; |
110 | fReplaceDialog.OnFind := OnFind; |
93 | fReplaceDialog.OnFind := OnFind; |
111 | fReplaceDialog.Options := fReplaceDialog.Options + [frHideWholeWord]; // TODO: currently not supported (see below) |
94 | fReplaceDialog.Options := fReplaceDialog.Options + [frHideWholeWord]; // TODO: currently not supported (see below) |
112 | - | ||
113 | {set find's default action on end of text to alert the user. |
- | |
114 | If a replace function changes this it is it's responsibility |
- | |
115 | to change it back} |
- | |
116 | FindActionOnEnd := frcAlertUser; |
- | |
117 | end; |
95 | end; |
118 | 96 | ||
119 | procedure TFindReplace.FindForwards( Sender: TFindDialog; start, finish: integer ); |
- | |
120 | var |
- | |
121 | i: integer; |
- | |
122 | - | ||
123 | begin |
- | |
124 | - | ||
125 | {to find the word we go through the text on a character by character |
- | |
126 | basis} |
- | |
127 | for i := start to finish do |
- | |
128 | if TryAndMatch( Sender, i, finish ) then |
- | |
129 | {if we've got a match then stop} |
- | |
130 | Exit; |
- | |
131 | - | ||
132 | end; |
- | |
133 | - | ||
134 | procedure TFindReplace.FindBackwards( Sender: TFindDialog; start, finish: Integer ); |
- | |
135 | {since only find has a (search) up option and replace doesn't |
- | |
136 | we don't have to worry about sender since only the onFind will |
- | |
137 | be calling this function} |
- | |
138 | - | ||
139 | var |
- | |
140 | i: integer; |
- | |
141 | - | ||
142 | begin |
- | |
143 | {See comments for findforward} |
- | |
144 | - | ||
145 | {to find the word we go through the text on a character by character |
- | |
146 | basis but working backwards} |
- | |
147 | for i := finish downto start do |
- | |
148 | if TryAndMatch( Sender, i, start ) then |
- | |
149 | Exit; |
- | |
150 | - | ||
151 | end; |
- | |
152 | - | ||
153 | function TFindReplace.TryAndMatch( Sender: TFindDialog; index, finish: integer ): boolean; |
- | |
154 | {returns true if there was a match and false otherwise} |
- | |
155 | var |
- | |
156 | StringToTest: string; |
- | |
157 | - | ||
158 | StringComparison: TReplaceFunc; |
- | |
159 | {the function to use to compare 2 strings. Should be assigned |
- | |
160 | different values according to the search criteria} |
- | |
161 | - | ||
162 | FindTextLength: integer; |
- | |
163 | - | ||
164 | resourcestring |
- | |
165 | S_CANT_BE_FOUND = '%s could not be found'; |
- | |
166 | begin |
- | |
167 | FindTextLength := Length( Sender.FindText ); |
- | |
168 | - | ||
169 | {create a new string to test against} |
- | |
170 | StringToTest := copy( fEditor.Text, index+1, FindTextLength ); |
- | |
171 | - | ||
172 | {assign a case sensitive or case insensitive string |
- | |
173 | comparison function to StringComparison depending |
- | |
174 | on the whether or not the user chose to match case. |
- | |
175 | The functions assigned are normal VCL functions.} |
- | |
176 | if frMatchCase in Sender.Options then |
- | |
177 | StringComparison := CompareStr |
- | |
178 | else |
- | |
179 | StringComparison := CompareText; |
- | |
180 | - | ||
181 | if (StringComparison( StringToTest, Sender.FindText ) = 0) and |
- | |
182 | TestWholeWord( Sender, copy( fEditor.Text, index, FindTextLength+2 ) ) then |
- | |
183 | {with TestWholeWord we pass the value index not index+1 so that it will also |
- | |
184 | get the previous character. We pass the value FindTextLenght+2 so it |
- | |
185 | will copy the next character after the test string aswell} |
- | |
186 | begin {if all true then we've found the text} |
- | |
187 | {highlight the word} |
- | |
188 | fEditor.SetFocus; |
- | |
189 | fEditor.SelStart := index; |
- | |
190 | fEditor.SelLength := FindTextLength; |
- | |
191 | - | ||
192 | {quit the function} |
- | |
193 | Result := true; {because we've found the word} |
- | |
194 | Exit; |
- | |
195 | end |
- | |
196 | {if we've tried the last character and we can't find it then |
- | |
197 | display a message saying so.} |
- | |
198 | else if (index = finish) and (FindActionOnEnd = frcAlertUser) then |
- | |
199 | ShowMessageFmt(S_CANT_BE_FOUND, [Sender.FindText]) |
- | |
200 | {otherwise if the replace function requested us to keep quiet |
- | |
201 | about it then don't display the message to the user} |
- | |
202 | else if (index = finish) and (FindActionOnEnd = frcAlertReplace) then |
- | |
203 | FindActionOnEnd := frcEndReached; |
- | |
204 | - | ||
205 | Result := false; {didn't find it} |
- | |
206 | end; |
- | |
207 | - | ||
208 | procedure TFindReplace.DoFind(dialog: TFindDialog; direction: TFindDirection); |
97 | function TSynEditFindReplace.GetDirection(dialog: TFindDialog): TFindDirection; |
209 | var |
- | |
210 | // highlightedText: pChar; |
- | |
211 | highlightedText: string; |
- | |
212 | - | ||
213 | begin |
- | |
214 | if direction = sdDefault then |
- | |
215 | begin |
98 | begin |
216 | if frDown in dialog.Options then |
99 | if frDown in dialog.Options then |
217 | direction := sdForwards |
100 | result := sdForwards |
218 | else |
101 | else |
219 | direction := sdBackwards; |
102 | result := sdBackwards; |
220 | end; |
103 | end; |
221 | 104 | ||
222 | {check if there is already some highlighted text. If there is and |
- | |
223 | this text is the text to search for then it's probably been highlighted |
- | |
224 | by the previous find operation. In this case, move selStart to |
- | |
225 | the position after the final character so the find operation won't find |
105 | procedure TSynEditFindReplace.DoFind(dialog: TFindDialog; direction: TFindDirection); |
226 | the same word again. If the user chose to search up then move selStart |
- | |
- | 106 | var |
|
227 | to the character before the highlighted word} |
107 | opt: TSynSearchOptions; |
228 | if fEditor.SelLength > 0 then |
108 | found: boolean; |
229 | begin |
109 | begin |
230 | (* |
- | |
231 | GetMem( highlightedText, fEditor.SelLength + 1 ); |
- | |
232 | fEditor.GetSelTextBuf( highlightedText, fEditor.SelLength+1 ); |
110 | if direction = sdDefault then direction := GetDirection(dialog); |
233 | *) |
- | |
234 | highlightedText := fEditor.SelText; |
- | |
235 | 111 | ||
236 | {compare the two strings} |
112 | if fEditor.SelAvail then |
237 | if StrIComp( PChar(highlightedText), pChar( dialog.FindText ) ) = 0 then |
- | |
238 | begin |
113 | begin |
239 | if direction = sdForwards then |
114 | if direction = sdForwards then |
240 | fEditor.selStart := fEditor.SelStart + fEditor.SelLength |
- | |
241 | else |
- | |
242 | fEditor.selStart := fEditor.SelStart - 1; |
- | |
243 | end; |
- | |
244 | - | ||
245 | (* |
- | |
246 | FreeMem( highlightedText, fEditor.SelLength + 1 ); |
- | |
247 | *) |
- | |
248 | end; |
- | |
249 | - | ||
250 | {begin the search} |
- | |
251 | if direction = sdForwards then {the user choose to search down} |
- | |
252 | begin |
115 | begin |
253 | {if the user has highlighted a block of text only search |
116 | fEditor.SelStart := fEditor.SelStart + 1; |
254 | within that block} |
- | |
255 | if fEditor.SelLength > 0 then |
117 | fEditor.SelLength := 0; |
256 | FindForwards( dialog, fEditor.selStart, fEditor.selStart + fEditor.selLength ) |
- | |
257 | {otherwise search the whole of the text} |
- | |
258 | else |
- | |
259 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); |
- | |
260 | end |
118 | end |
261 | else {the user chose to search up} |
- | |
262 | begin |
- | |
263 | {if the user has highlighted a block of text only search |
- | |
264 | within that block} |
- | |
265 | if fEditor.SelLength > 0 then |
- | |
266 | FindBackwards( dialog, fEditor.selStart, fEditor.selStart + fEditor.selLength ) |
- | |
267 | {otherwise search the whole of the text} |
- | |
268 | else |
119 | else |
269 | FindBackwards( dialog, 0, fEditor.selStart ); |
- | |
270 | end; |
- | |
271 | end; |
- | |
272 | - | ||
273 | procedure TFindReplace.OnFind(Sender: TObject); |
- | |
274 | var |
- | |
275 | FindDialog: TFindDialog; |
- | |
276 | begin |
120 | begin |
277 | FindDialog := Sender as TFindDialog; |
121 | // Links von Selektion springen |
278 | DoFind(FindDialog, sdDefault); |
122 | fEditor.SelLength := 0; |
- | 123 | end; |
|
279 | end; |
124 | end; |
280 | 125 | ||
- | 126 | opt := []; |
|
- | 127 | if frMatchCase in dialog.Options then Include(opt, ssoMatchCase); |
|
281 | procedure TFindReplace.OnReplace( Sender: TObject ); |
128 | if frWholeWord in dialog.Options then Include(opt, ssoWholeWord); |
282 | var |
- | |
283 | ReplaceDialog: TReplaceDialog; |
129 | //if frReplace in dialog.Options then Include(opt, ssoReplace); |
284 | begin |
- | |
285 | ReplaceDialog := Sender as TReplaceDialog; |
130 | //if frReplaceAll in dialog.Options then Include(opt, ssoReplaceAll); |
286 | - | ||
287 | {set the action on end to alert the function not the user} |
131 | if direction = sdBackwards then Include(opt, ssoBackwards); |
- | 132 | //Include(opt, ssoPrompt); // TODO: test. geht nicht? |
|
- | 133 | //if fEditor.SelAvail then Include(opt, ssoSelectedOnly); // TODO: geht nicht, weil er bei einer suche ja dann etwas selektirert und dann nicht weitergeht |
|
288 | FindActionOnEnd := frcAlertReplace; |
134 | Exclude(opt, ssoEntireScope); // TODO: ok? |
289 | 135 | ||
290 | // TODO: UnDo does not work |
136 | found := fEditor.SearchReplace(dialog.FindText, '', opt) > 0; |
291 | 137 | ||
292 | {now replace the word} |
138 | if not found then |
- | 139 | begin |
|
293 | if frReplace in ReplaceDialog.Options then |
140 | if direction = sdForwards then |
294 | DoReplace(ReplaceDialog) |
141 | ShowMessage('End of document reached.') |
295 | else |
142 | else |
296 | DoReplaceAll(ReplaceDialog); |
- | |
297 | - | ||
298 | {reset the action on end to alert the user} |
143 | ShowMessage('Begin of document reached.'); |
299 | FindActionOnEnd := frcAlertUser; |
- | |
300 | end; |
144 | end; |
301 | - | ||
302 | procedure TFindReplace.DoReplace(dialog: TReplaceDialog); |
- | |
303 | begin |
- | |
304 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); |
- | |
305 | TryAndReplace(dialog); |
- | |
306 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); // Jump to the next occurrence |
- | |
307 | end; |
145 | end; |
308 | 146 | ||
309 | procedure TFindReplace.DoReplaceAll(dialog: TReplaceDialog); |
147 | procedure TSynEditFindReplace.DoReplace(dialog: TReplaceDialog; direction: TFindDirection); |
- | 148 | var |
|
- | 149 | opt: TSynSearchOptions; |
|
- | 150 | numReplacements: integer; |
|
310 | begin |
151 | begin |
311 | {see comments for DoReplace} |
152 | if direction = sdDefault then direction := GetDirection(dialog); |
312 | 153 | ||
313 | fEditor.BeginUpdate; |
- | |
314 | fEditor.BeginUndoBlock; |
- | |
315 | try |
154 | opt := []; |
316 | {if the user has highlighted a block of text only replace |
155 | if frMatchCase in dialog.Options then Include(opt, ssoMatchCase); |
317 | within that block} |
- | |
318 | if fEditor.SelLength > 0 then |
- | |
319 | begin |
- | |
320 | // TODO: test this functionality |
156 | if frWholeWord in dialog.Options then Include(opt, ssoWholeWord); |
321 | FindForwards( dialog, fEditor.selStart, fEditor.selStart + fEditor.selLength ); |
157 | if frReplace in dialog.Options then Include(opt, ssoReplace); |
322 | {keep replacing until we reach the end of the text} |
158 | if frReplaceAll in dialog.Options then Include(opt, ssoReplaceAll); |
323 | while FindActionOnEnd <> frcEndReached do |
159 | if direction = sdBackwards then Include(opt, ssoBackwards); |
324 | begin |
- | |
325 | {we enclose the TryAndReplace in a loop because there might be more |
160 | Include(opt, ssoPrompt); // TODO: test. geht nicht? |
326 | than one occurence of the word in the line} |
161 | if fEditor.SelAvail then Include(opt, ssoSelectedOnly); |
327 | while TryAndReplace(dialog) do |
162 | Exclude(opt, ssoEntireScope); // TODO: ok? |
328 | FindForwards( dialog, fEditor.selStart, fEditor.selStart + fEditor.selLength ); |
- | |
329 | 163 | ||
330 | FindForwards( dialog, fEditor.selStart, fEditor.selStart + fEditor.selLength ); |
164 | fEditor.BeginUpdate; // TODO: geht nicht? |
331 | end; |
165 | //fEditor.BeginUndoBlock; |
332 | end |
166 | try |
333 | else {otherwise replace within the whole of the text} |
- | |
334 | begin |
- | |
335 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); |
167 | numReplacements := fEditor.SearchReplace(dialog.FindText, dialog.ReplaceText, opt); |
336 | while FindActionOnEnd <> frcEndReached do |
- | |
337 | begin |
- | |
338 | while TryAndReplace(dialog) do |
- | |
339 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); |
- | |
340 | - | ||
341 | FindForwards( dialog, fEditor.selStart, fEditor.GetTextLen ); |
- | |
342 | end; |
- | |
343 | end; |
- | |
344 | finally |
168 | finally |
- | 169 | //fEditor.EndUndoBlock; |
|
345 | fEditor.EndUpdate; |
170 | fEditor.EndUpdate; |
346 | fEditor.EndUndoBlock; |
- | |
347 | end; |
171 | end; |
348 | end; |
- | |
349 | - | ||
350 | function TFindReplace.TryAndReplace(dialog: TReplaceDialog): boolean; |
- | |
351 | {returns true if a replacement was made and false otherwise. This is |
- | |
352 | so a function can keep calling TryAndReplace until it returns false |
- | |
353 | since there might be more than one occurence of the word to replace |
- | |
354 | in the line} |
- | |
355 | - | ||
356 | var |
- | |
357 | LineNumber, ColumnNumber: integer; |
- | |
358 | - | ||
359 | OldSelStart: integer; {the position of the cursor prior to the text being replaced} |
- | |
360 | 172 | ||
- | 173 | // TODO: numReplacements anzeigen |
|
- | 174 | end; |
|
361 | 175 | ||
- | 176 | procedure TSynEditFindReplace.OnFind(Sender: TObject); |
|
362 | begin |
177 | begin |
363 | {assume no replacement was made} |
178 | DoFind(Sender as TFindDialog, sdDefault); |
364 | Result := false; |
179 | end; |
365 | 180 | ||
366 | {check to see if the word was found otherwise we don't add the |
- | |
367 | replaceText to the editor. That is, only delete the selected text |
- | |
368 | and insert the replacement text if the end was not reached} |
181 | procedure TSynEditFindReplace.OnReplace(Sender: TObject); |
369 | if not (FindActionOnEnd = frcEndReached) then |
- | |
370 | begin |
182 | begin |
371 | {get the line number and column number of the cursor which |
- | |
372 | is needed for string manipulations later. We should do this |
- | |
373 | before the call to clear selection} |
- | |
374 | - | ||
375 | // TODO: only replace beginning at the selected section / caret, not from beginning of the line!! |
- | |
376 | LineNumber := fEditor.CaretY-1; |
- | |
377 | ColumnNumber := fEditor.CaretX-1; |
- | |
378 | - | ||
379 | {get the position of the cursor prior to the replace operation |
- | |
380 | so we cab restore it later} |
- | |
381 | OldSelStart := fEditor.SelStart; |
- | |
382 | - | ||
383 | // Note: "fEditor.ClearSelection" can be used to delete the selected text |
- | |
384 | - | ||
385 | // TODO: only replace beginning at the selected section / caret, not from beginning of the line |
- | |
386 | // TODO: support "whole word" ? |
- | |
387 | if frMatchCase in dialog.Options then |
- | |
388 | fEditor.Lines[LineNumber] := StringReplace(fEditor.Lines[LineNumber], dialog.FindText, dialog.ReplaceText, []) |
- | |
389 | else |
- | |
390 | fEditor.Lines[LineNumber] := StringReplace(fEditor.Lines[LineNumber], dialog.FindText, dialog.ReplaceText, [rfIgnoreCase]); |
- | |
391 | - | ||
392 | {set the result to true since we have made a replacement} |
- | |
393 | Result := true; |
- | |
394 | - | ||
395 | {reposition the cursor to the character after the last chracter in |
- | |
396 | the newly replacing text. This is mainly so we can locate multiple |
- | |
397 | occurences of the to-be-replaced text in the same line} |
183 | DoReplace(Sender as TReplaceDialog, sdDefault); |
398 | fEditor.SelStart := oldSelStart + length( dialog.ReplaceText ); |
- | |
399 | end |
- | |
400 | end; |
184 | end; |
401 | 185 | ||
402 | procedure TFindReplace.FindExecute; |
186 | procedure TSynEditFindReplace.FindExecute; |
403 | begin |
187 | begin |
404 | fFindDialog.Execute; |
188 | fFindDialog.Execute; |
405 | end; |
189 | end; |
406 | 190 | ||
407 | procedure TFindReplace.ReplaceExecute; |
191 | procedure TSynEditFindReplace.ReplaceExecute; |
408 | begin |
192 | begin |
409 | fReplaceDialog.Execute; |
193 | fReplaceDialog.Execute; |
410 | end; |
194 | end; |
411 | 195 | ||
412 | function TFindReplace.TestWholeWord( Sender: TFindDialog; TestString: string ): boolean; |
- | |
413 | var |
- | |
414 | FindTextLength: integer; |
- | |
415 | begin |
- | |
416 | {assume it's not a whole word} |
- | |
417 | Result := false; |
- | |
418 | - | ||
419 | FindTextLength := Length( Sender.FindText ); |
- | |
420 | - | ||
421 | {if the user didn't choose whole words only then basically |
- | |
422 | we don't care about it so return true} |
- | |
423 | if not (frWholeWord in Sender.Options) then |
- | |
424 | begin |
- | |
425 | Result := true; |
- | |
426 | Exit; |
- | |
427 | end; |
- | |
428 | - | ||
429 | {Test if the word is a whole word} |
- | |
430 | {Basically there are 4 cases: ( _ denotes whitespace ) |
- | |
431 | 1. _word_ |
- | |
432 | 2. \nword_ |
- | |
433 | 3. _word\n |
- | |
434 | 4.\nword\n} |
- | |
435 | {case 1, note: #9 tab, #$A newline} |
- | |
436 | if (CharInSet(TestString[1], [' ', #9 ])) and (CharInSet(TestString[FindTextLength + 2], [' ', #9 ])) then |
- | |
437 | Result := true |
- | |
438 | {case 2} |
- | |
439 | else if(TestString[1] = #$A) and (CharInSet(TestString[FindTextLength + 2], [' ', #9 ])) then |
- | |
440 | Result := true |
- | |
441 | {case 3, note: #$D end of line} |
- | |
442 | else if(CharInSet(TestString[1], [' ', #9 ])) and (TestString[FindTextLength + 2] = #$D) then |
- | |
443 | Result := true |
- | |
444 | else if (TestString[1] = #$A) and (TestString[FindTextLength + 2] = #$D) then |
- | |
445 | Result := true |
- | |
446 | - | ||
447 | end; |
- | |
448 | - | ||
449 | procedure TFindReplace.FindContinue; |
196 | procedure TSynEditFindReplace.FindContinue; |
450 | begin |
197 | begin |
451 | if fFindDialog.FindText = '' then |
198 | if fFindDialog.FindText = '' then |
452 | begin |
199 | begin |
453 | fFindDialog.Options := fFindDialog.Options + [frDown]; // Default direction: down |
200 | fFindDialog.Options := fFindDialog.Options + [frDown]; // Default direction: down |
454 | FindExecute; |
201 | FindExecute; |
455 | end |
202 | end |
456 | else |
203 | else |
457 | DoFind(fFindDialog, sdDefault); |
204 | DoFind(fFindDialog, sdDefault); |
458 | end; |
205 | end; |
459 | 206 | ||
460 | procedure TFindReplace.FindNext; |
207 | procedure TSynEditFindReplace.FindNext; |
461 | begin |
208 | begin |
462 | if fFindDialog.FindText = '' then |
209 | if fFindDialog.FindText = '' then |
463 | begin |
210 | begin |
464 | fFindDialog.Options := fFindDialog.Options + [frDown]; |
211 | fFindDialog.Options := fFindDialog.Options + [frDown]; |
465 | FindExecute; |
212 | FindExecute; |
466 | end |
213 | end |
467 | else |
214 | else |
468 | DoFind(fFindDialog, sdForwards); |
215 | DoFind(fFindDialog, sdForwards); |
469 | end; |
216 | end; |
470 | 217 | ||
471 | procedure TFindReplace.FindPrev; |
218 | procedure TSynEditFindReplace.FindPrev; |
472 | begin |
219 | begin |
473 | if fFindDialog.FindText = '' then |
220 | if fFindDialog.FindText = '' then |
474 | begin |
221 | begin |
475 | fFindDialog.Options := fFindDialog.Options - [frDown]; |
222 | fFindDialog.Options := fFindDialog.Options - [frDown]; |
476 | FindExecute; |
223 | FindExecute; |
477 | end |
224 | end |
478 | else |
225 | else |
479 | DoFind(fFindDialog, sdBackwards); |
226 | DoFind(fFindDialog, sdBackwards); |
480 | end; |
227 | end; |
481 | 228 | ||
482 | procedure TFindReplace.GoToLine( LineNumber: integer ); |
229 | procedure TSynEditFindReplace.GoToLine(LineNumber: integer); |
483 | var |
230 | var |
484 | currentLine: integer; |
231 | currentLine: integer; |
485 | i: integer; |
232 | i: integer; |
486 | - | ||
487 | begin |
233 | begin |
488 | {set the current line to 1} |
- | |
489 | currentLine := 1; |
234 | currentLine := 1; |
490 | 235 | ||
491 | {go through the whole text looking for the line} |
- | |
492 | for i := 1 to fEditor.GetTextLen do |
236 | for i := 1 to fEditor.GetTextLen do |
493 | begin |
237 | begin |
494 | if currentLine = LineNumber then |
238 | if currentLine = LineNumber then |
495 | begin |
239 | begin |
496 | {goto the position corresponding to the line} |
- | |
497 | fEditor.selStart := i; |
240 | fEditor.selStart := i; |
498 | fEditor.SetFocus; |
241 | fEditor.SetFocus; |
499 | {quit the function} |
- | |
500 | Exit; |
242 | Exit; |
501 | end |
243 | end |
502 | else if fEditor.Text = #$D then |
244 | else if fEditor.Text = #$D then |
503 | inc( currentLine ); |
245 | inc(currentLine); |
504 | end; |
246 | end; |
505 | - | ||
506 | end; |
247 | end; |
507 | 248 | ||
508 | procedure TFindReplace.CloseDialogs; |
249 | procedure TSynEditFindReplace.CloseDialogs; |
509 | begin |
250 | begin |
510 | fFindDialog.CloseDialog; |
251 | fFindDialog.CloseDialog; |
511 | fReplaceDialog.CloseDialog; |
252 | fReplaceDialog.CloseDialog; |
512 | end; |
253 | end; |
513 | 254 | ||
514 | (* |
- | |
515 | procedure Register; |
- | |
516 | begin |
- | |
517 | RegisterComponents('Tek-tips', [TFindReplace]); |
- | |
518 | end; |
- | |
519 | *) |
- | |
520 | - | ||
521 | end. |
255 | end. |