-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathfindControl.pas
506 lines (450 loc) · 17.7 KB
/
findControl.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
(***
This unit contains the class TSearchBar which implements a Mozilla like
search bar.
@author Benito van der Zander (http://www.benibela.de)
*)
unit findControl;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$H+}
interface
uses
Classes, SysUtils, StdCtrls, ExtCtrls, Controls,Buttons,Graphics
{$ifdef lcl},LCLType,LCLIntf{$else},windows,messages{$endif};
{$IFNDEF LCL}
const
WM_USER_VISIBLE_CHANGED = WM_USER+1150;
{$ENDIF}
type
TFindState = set of (fsFound, fsLoopAround);
TSearchBarSubControl = (fscCloseButton, //**< This is a close button at the left side
fscCaption, //**< This is a label next to the close button and before the search edit
fscSelectLocation, //**< This is a combobox next to the search edit
fscSearchForward, //**< This is a forward search button next to the location combobox
fscSearchBackwards, //**< This is a backward search button next to the forward one
fscHighlight, //**< This is a highlight all button (which changes its down state)
fscStatus); //**< This is a label showing the search result state
TSearchBarSubControls = set of TSearchBarSubControl;
//**This event is called when something should be searched
//**@param incremental This is true when the new search continues an old one, e.g. it is called during typing for every pressed key
//**@param backwards This is true when the search should be occur backwards, e.g. the user press shift+enter or the backward search button
TSearchEvent = procedure (sender: TObject; incremental,backwards: boolean) of object;
{ TFindControl }
//todo: highlight shortcut, dblclick, doku, delphi
{ TSearchBar }
{** @abstract This class implements a Mozilla like search bar
You can use it this way:
@longCode(#
SearchBar:=TSearchBar.create(self); //replace self with the control which should contain the search bar
SearchBar.Parent:=self; //the search bar is automatically placeted at the bottom (align)
SearchBar.OnSearch:=SearchBarSearch; //this must be an event handler which is than called whenever something should be searched
#)
}
TSearchBar = class (TPanel)
private
FFindState: TFindState;
FFoundColor: TColor;
FHighlightChanged: TNotifyEvent;
FHighlighting: boolean;
FIgnoreNextKey:longint;
FLoopAroundState: string;
FNotFoundColor: TColor;
FNotFoundState: string;
FOldHeight: longint;
FCaption: string;
FOnClose: TNotifyEvent;
FOnSearch: TSearchEvent;
FOnShow: TNotifyEvent;
FsearchBackwardText: string;
FSearchForwardText: string;
FHighlightText:string;
FSubComponents: TSearchBarSubControls;
function GetHighlighting: boolean;
function GetSearchLocation: longint;
function GetSearchLocations: TStrings;
function GetSearchText: string;
procedure SetCaption(const AValue: string);
procedure SetFindState(const AValue: TFindState);
procedure SetHighlightText(const AValue: string);
procedure SetsearchBackwardText(const AValue: string);
procedure SetSearchForwardText(const AValue: string);
procedure SetSearchLocation(const AValue: longint);
procedure SetSubComponents(const AValue: TSearchBarSubControls);
procedure closeBtnClick(Sender: TObject);
procedure searchButtonClick(Sender: TObject);
procedure searchEdtKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure searchEdtKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure highlightClick(Sender: TObject);
procedure highlightBtnMouseUp(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
protected
closeBtn, searchForwardBtn, searchBackwardBtn, highlightBtn: TSpeedButton;
captionLbl,statusLabel: TLabel;
locationsCmb: TComboBox;
searchEdt: TEdit;
visibleSubControls: array of tcontrol;
procedure updateComponents;
procedure moveComponents;
{$ifdef lcl}
procedure DoOnResize; override;
// procedure ResizeDelayedAutoSizeChildren; override;
procedure SetVisible(Value: Boolean); override;
{$else}
procedure VisibleChanging; override;
procedure VisibleChangedMessage(var msg: TMsg); message WM_USER_VISIBLE_CHANGED;
{$endif}
procedure DoSearch(incremental, backwards: boolean);
public
property SearchText: string read GetSearchText; //**<This is the text to search, entered by the user
property SearchLocation: longint read GetSearchLocation write SetSearchLocation; //**< Currently selected combobox item
property SearchLocations: TStrings read GetSearchLocations; //**<ComboBox-items
property Highlighting: boolean read GetHighlighting; //**<State of the highlight all button
property FindState: TFindState read FFindState write SetFindState; //**< Set this to the result of the search operation
procedure SetFocus;override;//**< focuses the search text edit
constructor Create(TheOwner: TComponent);override;
destructor destroy();override;
published
property OnSearch: TSearchEvent read FOnSearch write FOnSearch; //**<This is called when the text should be searched, e.g. when the user clicks the buttons or types in the edit control
property OnClose: TNotifyEvent read FOnClose write FOnClose; //**< This is called when the search bar is closed (close button, or escape key)
property OnShow: TNotifyEvent read FOnShow write FOnShow; //**< This is called when the search bar is opened (setvisible/show)
property OnHighlightChanged: TNotifyEvent read FHighlightChanged write FHighlightChanged;//**< Called when the highlight button is pressed
property OnKeyDown; //**< Typical OnKeyDown-event. Setting key:=0, will prevent the default handling
property SubComponents: TSearchBarSubControls read FSubComponents write SetSubComponents; //**< This is a set specifies which sub components should be created @br The default is [fscCloseButton, fscCaption, fscSearchForward, fscSearchBackwards, fscStatus] @seealso TSearchBarSubControl
property Caption: string read FCaption write SetCaption; //**< @noautolink Caption of the search bar (needs fscCaption)
property SearchForwardText: string read FSearchForwardText write SetSearchForwardText; //**< @noautolink Caption of the button for forward search (needs fscSearchForward)
property SearchBackwardText: string read FsearchBackwardText write SetsearchBackwardText; //**< @noautolink Caption of the backward button (needs fscSearchBackwards)
property HighlightText: string read FHighlightText write SetHighlightText; //**< @noautolink Caption of the highlight all button (needs fscHighlight)
property FoundColor: TColor read FFoundColor write FFoundColor; //**< Background-color of the edit control when the text is found
property NotFoundColor: TColor read FNotFoundColor write FNotFoundColor; //**< Background-color of the edit control when the text is not found
property NotFoundState: string read FNotFoundState write FNotFoundState; //**< Text to display when the text is not found (needs fscStatus)
property LoopAroundState: string read FLoopAroundState write FLoopAroundState; //**< Text to display when the text has only been found after looping around (needs fscStatus)
end;
procedure Register;
implementation
uses math
{$ifdef lcl},LResources{$endif} //for icon
;
{ TSearchBar }
procedure TSearchBar.closeBtnClick(Sender: TObject);
begin
Visible:=false;
if assigned(OnClose) then OnClose(self);
end;
procedure TSearchBar.searchButtonClick(Sender: TObject);
begin
DoSearch(false,sender=searchBackwardBtn);
end;
procedure TSearchBar.searchEdtKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var oldKey: word;
begin
oldKey:=key;
if assigned(OnKeyDown) then begin
OnKeyDown(self,key,shift); //pass key down event
if (Key=0) and not (key in [VK_SHIFT,VK_CONTROL,VK_MENU]) then
FIgnoreNextKey:=oldkey;
end;
if key = 13 then begin
key:=0;
DoSearch(false,ssShift in shift);
end;
end;
procedure TSearchBar.searchEdtKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if key=FIgnoreNextKey then begin
FIgnoreNextKey:=0;
exit;
end;
case key of
0: ;
13: begin
key:=0;
//DoSearch(false,ssShift in shift);
end;
vk_escape: closeBtn.Click;
//VK_DOWN,VK_UP,VK_NEXT,VK_PRIOR:; //see key down
VK_SHIFT,VK_CONTROL,VK_MENU: ;
else begin
if assigned(OnKeyDown) then
OnKeyDown(self,key,shift); //pass key events
DoSearch(true,false);
end;
end;
end;
function TSearchBar.GetSearchText: string;
begin
if assigned(searchEdt) then result:=searchEdt.Text
else result:='';
end;
procedure TSearchBar.highlightClick(Sender: TObject);
begin
if GetHighlighting = FHighlighting then exit;
if assigned(FHighlightChanged) then
FHighlightChanged(self);
FHighlighting:=GetHighlighting;
end;
procedure TSearchBar.highlightBtnMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if GetHighlighting = FHighlighting then exit;
if assigned(FHighlightChanged) then
FHighlightChanged(self);
FHighlighting:=GetHighlighting;
end;
function TSearchBar.GetSearchLocation: longint;
begin
if locationsCmb=nil then result:=-1
else result:=locationsCmb.ItemIndex;
end;
function TSearchBar.GetHighlighting: boolean;
begin
Result:=(highlightBtn<>nil) and (highlightBtn.Down);
end;
function TSearchBar.GetSearchLocations: TStrings;
begin
if locationsCmb=nil then result:=nil
else result:=locationsCmb.Items;
end;
procedure TSearchBar.SetCaption(const AValue: string);
begin
if FCaption=AValue then exit;
FCaption:=AValue;
end;
procedure TSearchBar.SetFindState(const AValue: TFindState);
begin
FFindState:=AValue;
if assigned(searchEdt) then
if searchEdt.Text='' then searchEdt.Color:=clWindow
else if fsFound in AValue then searchEdt.Color:=FFoundColor
else searchEdt.Color:=FNotFoundColor;
if assigned(statusLabel) then begin
if not (fsFound in AValue) then statusLabel.Caption:=FNotFoundState
else if (fsLoopAround in AValue) then statusLabel.Caption:=FLoopAroundState
else statusLabel.Caption:='';
end;
end;
procedure TSearchBar.SetHighlightText(const AValue: string);
begin
if FHighlightText=AValue then exit;
FHighlightText:=AValue;
updateComponents;
end;
procedure TSearchBar.SetsearchBackwardText(const AValue: string);
begin
if FsearchBackwardText=AValue then exit;
FsearchBackwardText:=AValue;
updateComponents;
end;
procedure TSearchBar.SetSearchForwardText(const AValue: string);
begin
if FSearchForwardText=AValue then exit;
FSearchForwardText:=AValue;
updateComponents;
end;
procedure TSearchBar.SetSearchLocation(const AValue: longint);
begin
if locationsCmb<>nil then locationsCmb.ItemIndex:=AValue;
end;
procedure TSearchBar.SetSubComponents(const AValue: TSearchBarSubControls);
begin
if FSubComponents=AValue then exit;
FSubComponents:=AValue;
updateComponents;
end;
{$ifndef lcl}
type TControlCracker = class(TControl)
public
property caption;
end;
{$endif}
procedure TSearchBar.updateComponents;
var tempBitmap: graphics.TBitmap;
controlIndex: integer = 0;
//returns if the control is new created
function setControl(show: boolean; var control: TControl; controlClass: TControlClass; cap:string; wid: longint=-1): boolean;
begin
result:=false;
if show then begin
if control = nil then begin
control:=controlClass.Create(Self);
control.parent:=Self;
result:=true;
end;
control.Visible:=true;
{$ifndef lcl}TControlCracker{$endif}(control).Caption:=cap;
if tempBitmap.Canvas.TextWidth(cap) > wid-5 then
wid:=tempBitmap.Canvas.TextWidth(cap)+5;
if wid<>-1 then control.Width:=max(control.Height, wid);
visibleSubControls[controlIndex] := control;
inc(controlIndex);
end else if control<>nil then begin
control.free;
control:=nil;
end;
end;
begin
tempBitmap:=graphics.TBitmap.Create;
tempBitmap.Width:=8;
tempBitmap.Height:=8;
tempBitmap.Canvas.Font:=font;
tempBitmap.Canvas.TextOut(0,0,'load font'); //yes this is really needed, otherwise the lcl chrashes
SetLength(visibleSubControls, ord(high(TSearchBarSubControls)) - ord(low(TSearchBarSubControls)) + 1 + 1);
try
if setControl(fscCloseButton in SubComponents,tcontrol(closeBtn),TSpeedButton, 'X',20) then
closeBtn.OnClick:=closeBtnClick;
SetControl(fscCaption in SubComponents,tcontrol(captionLbl),TLabel, Caption);
if SetControl(true,tcontrol(searchEdt),TEdit, '',150) then begin
searchEdt.OnKeyDown:=searchEdtKeyDown;
searchEdt.OnKeyUp:=searchEdtKeyUp;
end;
if SetControl(fscSelectLocation in SubComponents,tcontrol(locationsCmb),TComboBox, '',100) then
locationsCmb.Style:=csDropDownList;
if SetControl(fscSearchForward in SubComponents,tcontrol(searchForwardBtn),TSpeedButton, SearchForwardText,75) then
searchForwardBtn.OnClick:=searchButtonClick;
if setControl(fscSearchBackwards in SubComponents,tcontrol(searchBackwardBtn),TSpeedButton,searchBackwardText,75) then
searchBackwardBtn.OnClick:=searchButtonClick;
if setControl(fscHighlight in SubComponents,tcontrol(highlightBtn),TSpeedButton,HighlightText,75) then begin
highlightBtn.OnClick:=highlightClick;
highlightBtn.OnMouseUp:=highlightBtnMouseUp;
highlightBtn.AllowAllUp:=true;
highlightBtn.GroupIndex:=1;
end;
setControl(fscStatus in SubComponents,tcontrol(statusLabel),TLabel,'');
SetLength(visibleSubControls, controlIndex);
moveComponents;
finally
tempBitmap.free;
end;
end;
procedure TSearchBar.moveComponents;
var i,maxHeight:longint;
totalWidth, editWidth: Integer;
const HSPACING:longint = 3;
minEditWidth = 150;
maxEditWidth = 450;
begin
maxHeight:=0;
for i:=0 to ControlCount -1 do
if Controls[i].Height>maxHeight then
maxHeight:=Controls[i].Height;
for i:=0 to ControlCount -1 do begin
if Controls[i] is TSpeedButton then
Controls[i].Height:=maxHeight;
Controls[i].Top:=(Height-Controls[i].Height) div 2;
end;
totalWidth := 0;
for i := 0 to high(visibleSubControls) do begin
if visibleSubControls[i] <> searchEdt then
totalWidth := totalWidth + visibleSubControls[i].Width + HSPACING;
end;
if assigned(searchEdt) then begin
editWidth := ClientWidth - totalWidth;
if editWidth < minEditWidth then editWidth := minEditWidth
else if editWidth > maxEditWidth then editWidth := maxEditWidth;
searchEdt.Width := editWidth;
end;
totalWidth := HSPACING;
for i := 0 to high(visibleSubControls) do begin
visibleSubControls[i].Left := totalWidth;
totalWidth := totalWidth + visibleSubControls[i].Width + HSPACING;
end;
end;
{$ifdef lcl}
procedure TSearchBar.DoOnResize;
begin
//if height<>FOldHeight then
moveComponents;
FOldHeight:=Height;
inherited DoOnResize;
end;
{procedure TSearchBar.ResizeDelayedAutoSizeChildren;
begin
inherited ResizeDelayedAutoSizeChildren;
//this is the first time the size of the caption label is known!
updateComponents;
end;}
procedure TSearchBar.SetVisible(Value: Boolean);
var changed: boolean;
begin
changed:=Visible<>value;
inherited SetVisible(Value);
if changed then
if not Visible and assigned(OnClose) then OnClose(self)
else if Visible and assigned(OnShow) then OnShow(self);
end;
{$else}
procedure TSearchBar.VisibleChanging;
begin
moveComponents;
FOldHeight:=Height;
postMessage(handle,WM_USER_VISIBLE_CHANGED,0,0);
inherited VisibleChanging;
end;
procedure TSearchBar.VisibleChangedMessage(var msg: TMsg);
begin
if Visible and assigned(OnClose) then OnShow(self)
else if not Visible and assigned(OnShow) then OnClose(self);
end;
{$endif}
procedure TSearchBar.DoSearch(incremental, backwards: boolean);
begin
if assigned(OnSearch) then OnSearch(self, incremental, backwards);
end;
procedure TSearchBar.setFocus;
begin
inherited setFocus;
if assigned(searchEdt) then searchEdt.SetFocus;
end;
constructor TSearchBar.Create(TheOwner: TComponent);
var
tempBitmap: graphics.TBitmap;
begin
inherited Create(TheOwner);
Align:=alBottom;
FsearchBackwardText:='&Previous';
FSearchForwardText:='&Next';
FHighlightText:='&Highlight all';
FCaption:='Find:';
FNotFoundState:='Not found!';
FLoopAroundState:='Loop around';
FSubComponents:=[];
SubComponents:=[fscCloseButton, fscCaption, fscSearchForward, fscSearchBackwards, fscStatus];
//size which seem to work well with the default font size
tempBitmap:=graphics.TBitmap.Create;
tempBitmap.Width:=8;
tempBitmap.Height:=8;
tempBitmap.Canvas.Font:=font;
tempBitmap.Canvas.TextOut(0,0,'load font'); //yes this is really needed, otherwise the lcl chrashes
Height:=2*tempBitmap.Canvas.TextHeight('^MH,')+3;
tempBitmap.free;
FHighlighting:=false;
FFoundColor:=$77DD77;
FNotFoundColor:=rgb($DD,$77,$77);
{$ifdef delphi}
{$ifndef VER100}{$ifndef VER120}{$ifndef VER130}{$ifndef VER140} //delphi 7 or higher
ControlStyle := controlstyle - [csparentbackground];
{$endif}{$endif}{$endif}{$endif}
{$endif}
end;
destructor TSearchBar.destroy();
begin
closeBtn.free;
searchForwardBtn.free;
searchBackwardBtn.free;
highlightBtn.free;
captionLbl.free;
statusLabel.free;
locationsCmb.free;
searchEdt.free;
inherited destroy();
end;
procedure Register;
begin
RegisterComponents('BeniBela', [TSearchBar]);
end;
{$ifdef lcl}
initialization
{$I findControl.lrs}
{$endif}
end.