1 {Frame que contiene utilidades a realizar sobre grillas TSTringGrid. Actualmente solo se
2 ha implementado la opción de búsqueda.
3 Para realizar su trabajo, se debe asociar a un TStringGrid cualquiera. Notar que no es
4 necesario usar el objeto TUtilGrilla o TUtilGrillaFil.
5 Para facilitar el uso de este frame, se ha definido que no tenga más dependencias
6 especiales que BasicGrilla.
7 Además no se debe interceptar eventos de la grilla, a menos que sea estrcitamente necesario.
8 Estos se hace previeniendo el caso de que la grilla necesite usar sus eventos o estos estén
9 siendo usados por alguna otra utilidad.
10
11 La forma de utilizar el frame, es insertándolo en el formulario o panel, a modo de
12 ToolBar. Luego configurarlo:
13
14 fraUtilsGrilla1.Inic(grilla);
15 fraUtilsGrilla1.AgregarColumnaFiltro('Por Código', 1);
16 fraUtilsGrilla1.AgregarColumnaFiltro('Por Nombre', 2);
17
18 También se debe llamar a fraUtilsGrilla1.GridKeyPress(Key), en el evento KeyPress() de la
19 grilla, si se desea que la búsqueda se haga con solo pulsar una tecla en la grilla.
20
21 Por Tito Hinostroza 28/04/016.
22 }
23 unit FrameFiltCampo;
24 {$mode objfpc}{$H+}
25 interface
26 uses
27 Classes, SysUtils, FileUtil, Forms, Controls, StdCtrls, ExtCtrls, LCLProc,
28 Graphics, Buttons, LCLType, Grids, fgl, UtilsGrilla;
29 type
30 TFiltroGrilla = class
31 etiq: string; //etiqueta a mostrar
32 campo: integer; //campo a usar como filtro
33 end;
34 TFiltroGrilla_list = specialize TFPGObjectList<TFiltroGrilla>;
35 //Modo de filtrado
36 TModFiltrado = (
37 mfilNone, //Sin filtro (pasa todo)
38 mfil1Pal, //COincidencia con 1 palabra
39 mfil2Pal, //Coincidencia con 2 palabras
40 mfilIgualN, //Comparación con valor
41 mfilMayorN, //Comparación con valor
42 mfilMenorN, //Comparación con valor
43 mfilMayorIN, //Comparación con valor
44 mfilMenorIN, //Comparación con valor
45 mfilDiferN //Comparación con valor
46 );
47 { TfraFiltCampo }
48 TfraFiltCampo = class(TFrame)
49 ComboBox2: TComboBox;
50 Edit1: TEdit;
51 Panel1: TPanel;
52 btnFind: TSpeedButton;
53 btnClose: TSpeedButton;
54
55 procedure btnCloseClick(Sender: TObject);
56
57 procedure ComboBox2Change(Sender: TObject);
Filtronull58 function Filtro(const f: integer):boolean;
59 procedure Edit1Change(Sender: TObject);
60 procedure Edit1Enter(Sender: TObject);
61 procedure Edit1Exit(Sender: TObject);
62 procedure Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
63 procedure FrameResize(Sender: TObject);
64 private
65 grilla :TStringGrid; //grilla asociada
66 proteger: boolean;
67 filtros : TFiltroGrilla_list;
68 campoAfiltrar: integer;
69 modFiltro: TModFiltrado; //Modo del filtro
70 buscar1, buscar2: string; //palabras de búsqueda
71 buscarN: double; //búsqueda por cantidad
72 griFiltrar: TUtilGrilla;
73 procedure ActualizarVisibilidadBotones;
74 procedure fraFiltCampoCambiaFiltro;
75 procedure ModoConTexto(txt0: string);
76 procedure ModoSinTexto;
77 procedure PreparaFiltro;
PreparaCadnull78 function PreparaCad(const cad: string): string;
79 public
80 msjeBuscar : string; //mensajae inicial que aparecerá en el cuadro de texto
81 incluirCateg: boolean;
82 txtBusq : string; //Texto que se usa para la búsqueda
83 OnCambiaFiltro: procedure of object; //Cuando cambia algún parámetro del filtro
84 procedure AgregarColumnaFiltro(NomCampo: string; ColCampo: integer);
85 procedure GridKeyPress(var Key: char); //Para dejar al frame procesar el evento KeyPress de la grilla
SinTextonull86 function SinTexto: boolean;
87 procedure LeerCamposDeGrilla(cols: TGrillaDBCol_list; indCampoDef: integer);
88 procedure Activar(txtIni: string);
89 procedure SetFocus; override;
Focusednull90 function Focused: Boolean; override;
91 public //Inicialización
92 procedure Inic(grilla0: TStringGrid); virtual;
93 procedure Inic(gri: TUtilGrilla; campoDef: integer=-1); virtual;
94 constructor Create(TheOwner: TComponent); override;
95 destructor Destroy; override;
96 end;
97
98 implementation
99 {$R *.lfm}
100 { TfraFiltCampo }
101 procedure TfraFiltCampo.ActualizarVisibilidadBotones;
102 begin
103 if txtBusq = '' then begin
104 //No tiene contenido
105 btnFind.Visible:=true;
106 btnClose.Visible:=false;
107 end else begin
108 //Tiene contendio
109 btnFind.Visible:=false;
110 btnClose.Visible:=true;
111 end;
112 //ComboBox2.Enabled := filtros.Count>0;
113 end;
114 procedure TfraFiltCampo.ModoSinTexto;
115 //Pone al control de edición en modo "Esperando a que se escriba."
116 begin
117 proteger := true;
118 Edit1.Font.Italic := true;
119 Edit1.Font.Color := clGray;
120 Edit1.Text := msjeBuscar;
121 proteger := false;
122 end;
123 procedure TfraFiltCampo.ModoConTexto(txt0: string);
124 begin
125 proteger := true;
126 Edit1.Font.Italic := false;
127 Edit1.Font.Color := clBlack;
128 Edit1.Text:=txt0;
129 proteger := false;
130 end;
TfraFiltCampo.SinTextonull131 function TfraFiltCampo.SinTexto: boolean;
132 {Indica si el control está sin texto de búsqueda.}
133 begin
134 Result := txtBusq='';
135 end;
136 procedure TfraFiltCampo.AgregarColumnaFiltro(NomCampo: string;
137 ColCampo: integer);
138 //Agrega un campo para usar como filtro a la lista
139 var
140 fil: TFiltroGrilla;
141 begin
142 fil := TFiltroGrilla.Create;
143 fil.etiq := NomCampo;
144 fil.campo := ColCampo;
145 filtros.Add(fil);
146 ComboBox2.AddItem(NomCampo, nil);
147 ComboBox2.ItemIndex:=0; //selecciona el primero
148 end;
149 procedure TfraFiltCampo.btnCloseClick(Sender: TObject); //Limpia texto
150 begin
151 Edit1.Text:= '';
152 end;
PreparaCadnull153 function TfraFiltCampo.PreparaCad(const cad: string): string;
154 {Procesa una cadena y la deja lista para comparaciones}
155 begin
156 Result := Upcase(trim(cad));
157 //Esta parte puede ser lenta
158 Result := StringReplace(Result, 'Á', 'A', [rfReplaceAll]);
159 Result := StringReplace(Result, 'É', 'E', [rfReplaceAll]);
160 Result := StringReplace(Result, 'Í', 'I', [rfReplaceAll]);
161 Result := StringReplace(Result, 'Ó', 'O', [rfReplaceAll]);
162 Result := StringReplace(Result, 'Ú', 'U', [rfReplaceAll]);
163 Result := StringReplace(Result, 'á', 'A', [rfReplaceAll]);
164 Result := StringReplace(Result, 'é', 'E', [rfReplaceAll]);
165 Result := StringReplace(Result, 'í', 'I', [rfReplaceAll]);
166 Result := StringReplace(Result, 'ó', 'O', [rfReplaceAll]);
167 Result := StringReplace(Result, 'ú', 'U', [rfReplaceAll]);
168 end;
Filtronull169 function TfraFiltCampo.Filtro(const f: integer): boolean;
170 {Rutina que implementa un filtro, de acuerdo al formato requerido por TUtilGrilla.
171 Esta rutina es llamada por cada fila de la grilla.}
ValidaFiltFil1null172 function ValidaFiltFil1(const busc: string; const buscar1: string): boolean;
173 begin
174 if Pos(buscar1, busc) <> 0 then begin
175 exit(true);
176 end else begin //no coincide
177 exit(false);
178 end;
179 end;
ValidaFiltFil2null180 function ValidaFiltFil2(const busc: string; const buscar1, buscar2: string): boolean;
181 begin
182 if Pos(buscar1, busc) <> 0 then begin
183 //coincide la primera palabra, vemos la segunda
184 if Pos(buscar2, busc) <> 0 then begin
185 exit(true);
186 end else begin //no coincide
187 exit(false);
188 end;
189 end else begin //no coincide
190 exit(false);
191 end;
192 end;
193 var
194 tmp: String;
195 n: Double;
196 begin
197 case modFiltro of
198 mfilNone: exit(true); //siempre pasa
199 mfil1Pal: begin //búsqueda de una palabra
200 tmp := PreparaCad(grilla.Cells[campoAfiltrar, f]);
201 exit(ValidaFiltFil1(tmp, buscar1));
202 end;
203 mfil2Pal: begin //búsqueda de dos palabras
204 tmp := PreparaCad(grilla.Cells[campoAfiltrar, f]);
205 exit(ValidaFiltFil2(tmp, buscar1, buscar2));
206 //se podría acelerar, si se evita pasar los parámetros "buscar1" y "buscar2".
207 end;
208 mfilIgualN: begin //igual a número
209 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
210 Result := (n = buscarN);
211 end;
212 mfilDiferN: begin
213 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
214 Result := (n <> buscarN);
215 end;
216 mfilMenorN: begin //igual a número
217 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
218 Result := (n < buscarN);
219 end;
220 mfilMayorN: begin //igual a número
221 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
222 Result := (n > buscarN);
223 end;
224 mfilMenorIN: begin //igual a número
225 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
226 Result := (n <= buscarN);
227 end;
228 mfilMayorIN: begin //igual a número
229 if not TryStrToFloat(grilla.Cells[campoAfiltrar, f], n) then exit(false);
230 Result := (n >= buscarN);
231 end;
232 end;
233 end;
234 procedure TfraFiltCampo.GridKeyPress(var Key: char);
235 {Debe ser llamado en el evento OnKeyPress, de la grilla, si se desee que el Frame tome el
236 control de este evento, iniciando el filtrado con la tecla pulsada.}
237 begin
238 if Key = #13 then exit; //este código, no debe ser considerado como tecla de edición
239 Edit1.SetFocus;
240 Edit1.Text := Key; //sobreescribe lo que hubiera
241 Edit1.SelStart:=length(Edit1.Text);
242 end;
243 procedure TfraFiltCampo.PreparaFiltro;
244 {Prepara }
245 const
246 MAX_PAL_BUS = 40; //tamaño máximo de las palabras de búsqueda, cuando hay más de una
247 var
248 p: Integer;
249 buscar, numStr: String;
250 begin
251 ActualizarVisibilidadBotones;
252 if (ComboBox2.ItemIndex = -1) or (grilla = nil) or (txtBusq='') then begin
253 modFiltro := mfilNone; //otra manera de decir que no hay filtro
254 end else if txtBusq[1] in ['=','>','<'] then begin
255 //Búsqueda por número
256 if length(txtBusq)<2 then begin //muy pocos caracteres
257 modFiltro := mfilNone;
258 exit;
259 end;
260 campoAfiltrar := filtros[ComboBox2.ItemIndex].campo;
261 //Identifica operador y número
262 case txtBusq[1] of
263 '=': begin
264 modFiltro := mfilIgualN; //otra manera de decir que no hay filtro
265 numStr := copy(txtBusq, 2, length(txtBusq));
266 end;
267 '<': begin
268 if txtBusq[2] = '>' then begin
269 numStr := copy(txtBusq, 3, length(txtBusq));
270 modFiltro := mfilDiferN;
271 end else if txtBusq[2] = '=' then begin
272 numStr := copy(txtBusq, 3, length(txtBusq));
273 modFiltro := mfilMenorIN;
274 end else begin
275 numStr := copy(txtBusq, 2, length(txtBusq));
276 modFiltro := mfilMenorN;
277 end;
278 end;
279 '>': begin
280 if txtBusq[2] = '=' then begin
281 numStr := copy(txtBusq, 3, length(txtBusq));
282 modFiltro := mfilMayorIN;
283 end else begin
284 numStr := copy(txtBusq, 2, length(txtBusq));
285 modFiltro := mfilMayorN;
286 end;
287 end;
288 end;
289 //Lee número a comparar
290 if not TryStrToFloat(numStr, buscarN) then begin
291 modFiltro := mfilNone;
292 exit;
293 end;
294 end else begin
295 buscar := PreparaCad(txtBusq); //simplifica
296 campoAfiltrar := filtros[ComboBox2.ItemIndex].campo;
297 p := pos(' ', buscar);
298 if p = 0 then begin
299 //solo hay una palabra de búsqueda
300 modFiltro := mfil1Pal; //otra manera de decir que no hay filtro
301 buscar1 := buscar;
302 end else begin
303 //Hay dos o mas palabras de búsqueda
304 modFiltro := mfil2Pal; //otra manera de decir que no hay filtro
305 buscar1 := copy(buscar,1,p-1);
306 while buscar[p]= ' ' do
307 inc(p); //Salta espacios. No debería terminar en espacio, porque ya se le aplicó trim()
308 buscar2 := copy(buscar, p, MAX_PAL_BUS); //solo puede leer hasta 40 caracteres
309 end;
310 end;
311 end;
312 procedure TfraFiltCampo.ComboBox2Change(Sender: TObject);
313 begin
314 PreparaFiltro;
315 if OnCambiaFiltro<>nil then OnCambiaFiltro;
316 end;
317 procedure TfraFiltCampo.Edit1Change(Sender: TObject);
318 begin
319 if proteger then exit; //para no actualizar
320 txtBusq := Edit1.Text;
321 PreparaFiltro;
322 if OnCambiaFiltro<>nil then OnCambiaFiltro;
323 end;
324 procedure TfraFiltCampo.Edit1Enter(Sender: TObject);
325 begin
326 //if (txtBusq='') and (Edit1.Text=msjeBuscar) then begin
327 ModoConTexto(txtBusq);
328 //end;
329 end;
330 procedure TfraFiltCampo.Edit1Exit(Sender: TObject);
331 begin
332 if txtBusq = '' then begin //No tiene contenido
333 ModoSinTexto;
334 end;
335 ActualizarVisibilidadBotones;
336 end;
337 procedure TfraFiltCampo.Edit1KeyDown(Sender: TObject; var Key: Word;
338 Shift: TShiftState);
339 begin
340 //Pasa el evento
341 if OnKeyDown<>nil then OnKeyDown(Sender, Key, Shift);
342 end;
343 procedure TfraFiltCampo.FrameResize(Sender: TObject);
344 begin
345 Panel1.Height:=Edit1.Height;
346 Panel1.Left:=0;
347 Panel1.Top :=0;
348 Panel1.Width:=self.Width - ComboBox2.Width;
349 ComboBox2.Top:=0;
350 ComboBox2.Left := self.Width - ComboBox2.Width;
351 //Edit1.Align:=alClient;
352 Edit1.Left := 0; //a la izquierda, dentro del panel
353 Edit1.Width := Panel1.Width - btnClose.Width; {Se supone que solo habrá un botón
354 visible, así que esto debe funcionar}
355 Edit1.Top:=2;
356 end;
357 procedure TfraFiltCampo.LeerCamposDeGrilla(cols: TGrillaDBCol_list; indCampoDef: integer);
358 {Configura todos los campos definidos, menos el 0, como campos para la búsqueda. Solo
359 se puede aplicar cuando se ha definido un objeto TfraUtilsGrilla, mediante UsarFrameUtils()
360 }
361 var
362 c: Integer;
363 begin
364 //Agrega filtro de todos los campos, menos el primero
365 for c:=1 to cols.Count-1 do begin
366 AgregarColumnaFiltro('Por ' + cols[c].nomCampo, c);
367 end;
368 ComboBox2.ItemIndex:=indCampoDef;
369 end;
370 procedure TfraFiltCampo.fraFiltCampoCambiaFiltro;
371 begin
372 if griFiltrar<>nil then griFiltrar.Filtrar;
373 end;
374 procedure TfraFiltCampo.Activar(txtIni: string);
375 {Activa el panel de búsqueda, dándole el enfoque, y poniendo un texto debúsqueda inicial. }
376 begin
377 Edit1.Text:=txtIni;
378 if Edit1.Visible then Edit1.SetFocus;
379 Edit1.SelStart:=2;
380 end;
381 procedure TfraFiltCampo.SetFocus;
382 begin
383 // inherited SetFocus;
384 try
385 edit1.SetFocus;
386 except
387 end;
388 end;
TfraFiltCampo.Focusednull389 function TfraFiltCampo.Focused: Boolean;
390 begin
391 Result := inherited Focused;
392 if Edit1.Focused then Result := true;
393 end;
394 procedure TfraFiltCampo.Inic(grilla0: TStringGrid);
395 {Prepara al frame para iniciar su trabajo. Notar que para evitar conflictos, se ha
396 definido que no se intercepten los eventos de la grilla, en este Frame.}
397 begin
398 grilla := grilla0;
399 ComboBox2.Clear;
400 filtros.Clear;
401 end;
402 procedure TfraFiltCampo.Inic(gri: TUtilGrilla; campoDef: integer = -1);
403 {Asocia al frame para trabajar con un objeto TUtilGrilla. "campoDef" es el campo por
404 defecto que se usará cuando se muestre el filtro.}
405 begin
406 Inic(gri.grilla);
407 if campoDef>=0 then begin
408 LeerCamposDeGrilla(gri.cols, campoDef);
409 end;
410 gri.AgregarFiltro(@Filtro); //agrega su filtro
411 {Crea un manejador de evento temporal, para ejecutar el filtro. Este evento puede
412 luego reasignarse, de acuerdo a necesida, ya que no es vital. }
413 griFiltrar := gri;
414 OnCambiaFiltro:=@fraFiltCampoCambiaFiltro;
415 end;
416 constructor TfraFiltCampo.Create(TheOwner: TComponent);
417 begin
418 inherited Create(TheOwner);
419 OnResize:=@FrameResize;
420 filtros := TFiltroGrilla_list.Create(true);
421 msjeBuscar := 'Texto a buscar'; //Inicia mensaje
422 ActualizarVisibilidadBotones;
423 ModoSinTexto;
424 end;
425 destructor TfraFiltCampo.Destroy;
426 begin
427 filtros.Destroy;
428 inherited Destroy;
429 end;
430
431 end.
432
433