Hi Boki
I have been playing with SearchNext() some more and there are still problems in the way it's been implemented.
For example:
When the SearchNext() function reaches the last row a grid, it automatically "rolls over" to the first row of the grid and continues the search.
The following code will always cause an endless loop:
CODE
while SearcNext() do
....
because even if there is only one row in the grid that matches the search criteria, it will keep finding the same row over and over again.
Take a look at these changes:
CODE
TSearchOptions = set of (soCaseInsensitive, soPartialKey);
CODE
function Roll(FromRow, ToRow: Integer; SearchOptions: TSearchOptions): Boolean;
var
i: Integer;
begin
Result := False;
for i := FromRow to ToRow do
begin
if soCaseInsensitive in SearchOptions then
begin
if soPartialKey in SearchOptions then
Result := WideCompareText(LeftStr(Cells[ColumnIndex, i], Length(Value)) , Value) = 0
else
Result := WideCompareText(Cells[ColumnIndex, i], Value) = 0;
end
else
begin
if soPartialKey in SearchOptions then
Result := WideCompareStr(LeftStr(Cells[ColumnIndex, i], Length(Value)) , Value) = 0
else
Result := WideCompareStr(Cells[ColumnIndex, i], Value) = 0;
end;
if Result then
begin
SelectedRow := i;
if not IsUpdating then ScrollToRow(i);
Break;
end;
end;
end;
I can now choose whether or not I want partial matches, and I can specify whether or not the search should be case-sensitive.
CODE
function TNxCustomGrid.SearchFirst(ColumnIndex: Integer; Value: WideString;
SearchOptions: TSearchOptions = []): Boolean;
begin
Result := False;
if RowCount = 0 then Exit;
Result := Roll(0, Pred(RowCount), SearchOptions);
end;
SearchFirst always starts from the first row, and then searches for the first match (if any).
CODE
function TNxCustomGrid.SearchNext(ColumnIndex: Integer; Value: WideString;
SearchOptions: TSearchOptions = []): Boolean;
begin
Result := False;
if RowCount = 0 then Exit;
Result := Roll(Succ(SelectedRow), Pred(RowCount), SearchOptions);
end;
SearcNext always starts at Succ(SelectedRow) and then searches
only to the end of the grid. We want to find the next match, but exclude any matches we have already found.
Now we can use these functions recursively, and we know that we won't find the same row twice.
CODE
Matches := 0;
if SearchFirst(ACol, Value, []) then
begin
Inc(Matches);
while SearchNext(ACol, Value, []) do
Inc(Matches);
end;
I also added a Locate function:
CODE
function TNxCustomGrid.Locate(ColumnIndex: Integer; Value: WideString;
SearchOptions: TSearchOptions = []): Boolean;
begin
Result := False;
if RowCount = 0 then Exit;
Result := Roll(SelectedRow, Pred(RowCount), SearchOptions) or Roll(0,SelectedRow, SearchOptions);
end;
Locate is very similar to SearchFirst, but it starts searching on the SelectedRow. If the row we are looking for is already selected, the function returns immediately without searching the whole grid, so it is good if we simply want to know if a match exists, but we don't care if it's the first match or about the position of row.
I think using the SearchFirst --> SearchNext setup makes the functions more usefull, and SearchOptions makes it a lot more flexible. What do you think?
Regards,
Deon