diff --git a/components/lazcontrols/listfilteredit.pas b/components/lazcontrols/listfilteredit.pas index 0f0211fcb1..cbde1a88e1 100644 --- a/components/lazcontrols/listfilteredit.pas +++ b/components/lazcontrols/listfilteredit.pas @@ -160,27 +160,12 @@ procedure TListFilterEdit.SortAndFilter; var Origi, i: Integer; Capt, FilterLC: string; - Pass, Done: Boolean; begin - Done:=False; fSortedData.Clear; FilterLC:=UTF8LowerCase(Filter); for Origi:=0 to fOriginalData.Count-1 do begin Capt:=fOriginalData[Origi]; - - // Filter with event handler if there is one. - if Assigned(fOnFilterItemEx) then - Pass:=fOnFilterItemEx(Capt, fOriginalData.Objects[Origi], Done) - else - Pass:=False; - // Support also the old filter event without a caption. - if (not (Pass and Done)) and Assigned(fOnFilterItem) then - Pass:=fOnFilterItem(fOriginalData.Objects[Origi], Done); - - // Filter by item's caption text if needed. - if not (Pass or Done) then - Pass:=(FilterLC='') or (Pos(FilterLC,UTF8LowerCase(Capt))>0); - if Pass then begin + if DoFilterItem(Capt, fOriginalData.Objects[Origi], FilterLC) then begin i:=fSortedData.Count-1; // Always sort the data. while i>=0 do begin if CompareFNs(Capt,fSortedData[i])>=0 then break; diff --git a/components/lazcontrols/listviewfilteredit.pas b/components/lazcontrols/listviewfilteredit.pas index 8dd2a7246c..00ba6a1b58 100644 --- a/components/lazcontrols/listviewfilteredit.pas +++ b/components/lazcontrols/listviewfilteredit.pas @@ -12,6 +12,7 @@ unit ListViewFilterEdit; {$mode objfpc}{$H+} +{$modeswitch advancedrecords} interface @@ -24,7 +25,13 @@ type //TImageIndexEvent = function (Str: String; Data: TObject; // var IsEnabled: Boolean): Integer of object; TStringArray = array of string; - TListViewDataList = specialize TFPGList; + TListViewDataItem = record + Data: Pointer; + StringArray: TStringArray; + + class operator =(a,b : TListViewDataItem) : Boolean; + end; + TListViewDataList = specialize TFPGList; { TListViewFilterEdit } @@ -40,7 +47,7 @@ type fOriginalData: TListViewDataList; // Data sorted for viewing. fFilteredData: TListViewDataList; - function MatchesFilter(aData: TStringArray): Boolean; + function MatchesFilter(aData: TListViewDataItem; const FilterLC: string): Boolean; procedure SetFilteredListview(const AValue: TCustomListView); protected procedure MoveTo(AIndex: Integer; ASelect: Boolean); @@ -73,6 +80,13 @@ var implementation +{ TListViewDataItem } + +class operator TListViewDataItem. = (a, b: TListViewDataItem): Boolean; +begin + Result := (a.Data=b.Data) and (a.StringArray=b.StringArray); +end; + { TListViewFilterEdit } constructor TListViewFilterEdit.Create(AOwner: TComponent); @@ -104,23 +118,25 @@ begin Result := -1; end; -function ListItem2Data(AItem: TListItem): TStringArray; +function ListItem2Data(AItem: TListItem): TListViewDataItem; var i: Integer; begin - SetLength(Result, AItem.SubItems.Count+1); - Result[0] := AItem.Caption; + Result.Data := AItem.Data; + SetLength(Result.StringArray, AItem.SubItems.Count+1); + Result.StringArray[0] := AItem.Caption; for i := 0 to AItem.SubItems.Count-1 do - Result[i+1] := AItem.SubItems[i]; + Result.StringArray[i+1] := AItem.SubItems[i]; end; -procedure Data2ListItem(AData: TStringArray; AItem: TListItem); +procedure Data2ListItem(AData: TListViewDataItem; AItem: TListItem); var i: Integer; begin - AItem.Caption := AData[0]; - for i := 1 to Length(AData)-1 do - AItem.SubItems.Add(AData[i]); + AItem.Data := AData.Data; + AItem.Caption := AData.StringArray[0]; + for i := 1 to Length(AData.StringArray)-1 do + AItem.SubItems.Add(AData.StringArray[i]); end; procedure TListViewFilterEdit.SetFilteredListview(const AValue: TCustomListView); @@ -134,23 +150,18 @@ begin fOriginalData.Add(ListItem2Data(fFilteredListview.Items[i])); end; -function TListViewFilterEdit.MatchesFilter(aData: TStringArray): Boolean; +function TListViewFilterEdit.MatchesFilter(aData: TListViewDataItem; + const FilterLC: string): Boolean; var i, EndInd: Integer; - FilterLC: string; begin - if Filter='' then - Exit(True); - FilterLC := UTF8LowerCase(Filter); if fByAllFields then - EndInd := Pred(Length(aData)) + EndInd := Pred(Length(aData.StringArray)) else EndInd := 0; for i := 0 to EndInd do begin - // ToDo: Use OnFilterItemEx event. - Result := Pos(FilterLC,UTF8LowerCase(aData[i]))>0; - if Result then - Exit; + if DoFilterItem(aData.StringArray[i], TObject(aData.Data), FilterLC) then + Exit(True); end; Result := False; end; @@ -173,12 +184,14 @@ procedure TListViewFilterEdit.SortAndFilter; // Copy data from fOriginalData to fSortedData in sorted order var Origi: Integer; - Data: TStringArray; + Data: TListViewDataItem; + FilterLC: string; begin fFilteredData.Clear; + FilterLC := UTF8LowerCase(Filter); for Origi:=0 to fOriginalData.Count-1 do begin Data:=fOriginalData[Origi]; - if MatchesFilter(Data) then + if MatchesFilter(Data, FilterLC) then fFilteredData.Add(Data); end; end; diff --git a/components/lazcontrols/treefilteredit.pas b/components/lazcontrols/treefilteredit.pas index 7e2bf4e729..24757c7c3e 100644 --- a/components/lazcontrols/treefilteredit.pas +++ b/components/lazcontrols/treefilteredit.pas @@ -460,26 +460,14 @@ function TTreeFilterEdit.FilterTree(Node: TTreeNode): Boolean; // Filter all tree branches recursively, setting Node.Visible as needed. // Returns True if Node or its siblings or child nodes have visible items. var - Pass, Done: Boolean; + Pass: Boolean; FilterLC: string; begin Result:=False; - Done:=False; FilterLC:=UTF8LowerCase(Filter); while Node<>nil do begin - // Filter with event handler if there is one. - if Assigned(fOnFilterItemEx) then - Pass:=fOnFilterItemEx(Node.Text, TObject(Node.Data), Done) - else - Pass:=False; - // Support also the old filter event without a caption. - if (not (Pass and Done)) and Assigned(fOnFilterItem) then - Pass:=fOnFilterItem(TObject(Node.Data), Done); - - // Filter by item's caption text if needed. - if not (Pass or Done) then - Pass:=(FilterLC='') or (Pos(FilterLC,UTF8LowerCase(Node.Text))>0); + Pass := DoFilterItem(Node.Text, TObject(Node.Data), FilterLC); if Pass and (fFirstPassedNode=Nil) then fFirstPassedNode:=Node; // Recursive call for child nodes. diff --git a/lcl/editbtn.pas b/lcl/editbtn.pas index d2647bb2ed..2e7a9b1e53 100644 --- a/lcl/editbtn.pas +++ b/lcl/editbtn.pas @@ -222,6 +222,10 @@ type fOnFilterItem: TFilterItemEvent; fOnFilterItemEx: TFilterItemExEvent; fOnCheckItem: TCheckItemEvent; + function DoFilterItem(const ACaption: string; const Item: TObject; + const FilterLC: string): Boolean; virtual; + function DoDefaultFilterItem(const ACaption: string; const Item: TObject; + const FilterLC: string): Boolean; virtual; procedure EditKeyDown(var Key: Word; Shift: TShiftState); override; procedure EditChange; override; procedure EditEnter; override; @@ -1118,6 +1122,33 @@ begin inherited Destroy; end; +function TCustomControlFilterEdit.DoDefaultFilterItem(const ACaption: string; + const Item: TObject; const FilterLC: string): Boolean; +begin + Result := (FilterLC='') or (Pos(FilterLC,UTF8LowerCase(ACaption))>0); +end; + +function TCustomControlFilterEdit.DoFilterItem(const ACaption: string; + const Item: TObject; const FilterLC: string): Boolean; +var + Done: Boolean; +begin + Done := False; + + // Filter with event handler if there is one. + if Assigned(fOnFilterItemEx) then + Result:=fOnFilterItemEx(ACaption, Item, Done) + else + Result:=False; + // Support also the old filter event without a caption. + if (not (Result and Done)) and Assigned(fOnFilterItem) then + Result:=fOnFilterItem(Item, Done); + + // Filter by item's caption text if needed. + if not (Result or Done) then + Result:=DoDefaultFilterItem(ACaption, Item, FilterLC); +end; + procedure TCustomControlFilterEdit.OnIdle(Sender: TObject; var Done: Boolean); begin if fNeedUpdate then