mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-09 20:36:48 +02:00
Improve ListFilterEdit, make it more object oriented
git-svn-id: trunk@31539 -
This commit is contained in:
parent
7bf73f4406
commit
ebb595fe19
@ -16,6 +16,56 @@ type
|
|||||||
TImageIndexEvent = function (Str: String; Data: TObject;
|
TImageIndexEvent = function (Str: String; Data: TObject;
|
||||||
var IsEnabled: Boolean): Integer of object;
|
var IsEnabled: Boolean): Integer of object;
|
||||||
|
|
||||||
|
TListFilterEdit = class;
|
||||||
|
|
||||||
|
{ TViewControl }
|
||||||
|
|
||||||
|
// A wrapper for the visual control displaying data
|
||||||
|
TViewControl = class
|
||||||
|
private
|
||||||
|
fOwner: TListFilterEdit;
|
||||||
|
fImgIndex: Integer;
|
||||||
|
fSortedData: TStringList;
|
||||||
|
function CompareFNs(AFilename1,AFilename2: string): integer;
|
||||||
|
procedure SortAndFilter;
|
||||||
|
procedure ApplyFilter; virtual; abstract;
|
||||||
|
procedure StoreSelection; virtual; abstract;
|
||||||
|
procedure RestoreSelection; virtual; abstract;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TListFilterEdit);
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TViewControlTreeview }
|
||||||
|
|
||||||
|
TViewControlTreeview = class(TViewControl)
|
||||||
|
private
|
||||||
|
fTreeview: TTreeview;
|
||||||
|
fTVNodeStack: TFPList;
|
||||||
|
procedure ApplyFilter; override;
|
||||||
|
procedure StoreSelection; override;
|
||||||
|
procedure RestoreSelection; override;
|
||||||
|
procedure FreeTVNodeData(Node: TTreeNode);
|
||||||
|
procedure TVDeleteUnneededNodes(p: integer);
|
||||||
|
procedure TVClearUnneededAndCreateHierachy(Filename: string);
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TListFilterEdit; aTreeview: TTreeview);
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TViewControlListbox }
|
||||||
|
|
||||||
|
TViewControlListbox = class(TViewControl)
|
||||||
|
private
|
||||||
|
fListbox: TListbox;
|
||||||
|
procedure ApplyFilter; override;
|
||||||
|
procedure StoreSelection; override;
|
||||||
|
procedure RestoreSelection; override;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TListFilterEdit; aListbox: TListbox);
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TListFilterEdit }
|
{ TListFilterEdit }
|
||||||
|
|
||||||
TListFilterEdit = class(TCustomEditButton)
|
TListFilterEdit = class(TCustomEditButton)
|
||||||
@ -30,10 +80,12 @@ type
|
|||||||
// A control showing the (filtered) data. These are exclusive, only one is used.
|
// A control showing the (filtered) data. These are exclusive, only one is used.
|
||||||
fFilteredTreeview: TTreeview;
|
fFilteredTreeview: TTreeview;
|
||||||
fFilteredListbox: TListbox;
|
fFilteredListbox: TListbox;
|
||||||
fImageIndexDirectory: integer; // Needed if directory structure is shown.
|
fViewControlWrapper: TViewControl; // Wraps either TTreeview or TListbox.
|
||||||
|
fImageIndexDirectory: integer; // Needed if directory structure is shown.
|
||||||
fImages4Listbox: TCustomImageList; // Listbox does not have ImageList of its own.
|
fImages4Listbox: TCustomImageList; // Listbox does not have ImageList of its own.
|
||||||
fNeedUpdate: boolean;
|
fNeedUpdate: Boolean;
|
||||||
fIdleConnected: boolean;
|
fIdleConnected: Boolean;
|
||||||
|
fIsFirstUpdate: Boolean;
|
||||||
fSelectedPart: TObject; // Select this node on next update
|
fSelectedPart: TObject; // Select this node on next update
|
||||||
fSelectionList: TStringList; // or store/restore the old selections here.
|
fSelectionList: TStringList; // or store/restore the old selections here.
|
||||||
fShowDirHierarchy: Boolean; // Show direcories / files as a tree structure.
|
fShowDirHierarchy: Boolean; // Show direcories / files as a tree structure.
|
||||||
@ -41,30 +93,17 @@ type
|
|||||||
// Full filename in node data is needed when showing the directory hierarchy.
|
// Full filename in node data is needed when showing the directory hierarchy.
|
||||||
// It is stored automatically if the map is populated by MapShortToFullFilename.
|
// It is stored automatically if the map is populated by MapShortToFullFilename.
|
||||||
fFilenameMap: TStringToStringTree;
|
fFilenameMap: TStringToStringTree;
|
||||||
fTVNodeStack: TFPList;
|
|
||||||
// Data supplied by caller through Data property.
|
// Data supplied by caller through Data property.
|
||||||
// Objects property is passed to OnGetImageIndex.
|
// Objects property is passed to OnGetImageIndex.
|
||||||
fOriginalData: TStringList;
|
fOriginalData: TStringList;
|
||||||
// Data sorted for viewing.
|
// Data sorted for viewing.
|
||||||
fSortedData: TStringList;
|
|
||||||
fRootNode: TTreeNode; // The filtered items are under this node.
|
fRootNode: TTreeNode; // The filtered items are under this node.
|
||||||
fOnGetImageIndex: TImageIndexEvent;
|
fOnGetImageIndex: TImageIndexEvent;
|
||||||
procedure SetFilter(const AValue: string);
|
procedure SetFilter(const AValue: string);
|
||||||
procedure SetFilteredTreeview(const AValue: TTreeview);
|
procedure SetFilteredTreeview(const AValue: TTreeview);
|
||||||
procedure SetFilteredListbox(const AValue: TListBox);
|
procedure SetFilteredListbox(const AValue: TListBox);
|
||||||
procedure SetShowDirHierarchy(const AValue: Boolean);
|
procedure SetShowDirHierarchy(const AValue: Boolean);
|
||||||
procedure StoreTreeSelection;
|
procedure SetIdleConnected(const AValue: Boolean);
|
||||||
procedure StoreListSelection;
|
|
||||||
procedure RestoreTreeSelection;
|
|
||||||
procedure RestoreListSelection;
|
|
||||||
procedure FreeTVNodeData(Node: TTreeNode);
|
|
||||||
procedure TVDeleteUnneededNodes(p: integer);
|
|
||||||
procedure TVClearUnneededAndCreateHierachy(Filename: string);
|
|
||||||
function CompareFNs(AFilename1,AFilename2: string): integer;
|
|
||||||
procedure SortAndFilter;
|
|
||||||
procedure ApplyFilterToListBox;
|
|
||||||
procedure ApplyFilterToTreeview;
|
|
||||||
procedure SetIdleConnected(const AValue: boolean);
|
|
||||||
protected
|
protected
|
||||||
function GetDefaultGlyph: TBitmap; override;
|
function GetDefaultGlyph: TBitmap; override;
|
||||||
function GetDefaultGlyphName: String; override;
|
function GetDefaultGlyphName: String; override;
|
||||||
@ -78,8 +117,9 @@ type
|
|||||||
public
|
public
|
||||||
property Filter: string read fFilter write SetFilter;
|
property Filter: string read fFilter write SetFilter;
|
||||||
property ImageIndexDirectory: integer read fImageIndexDirectory write fImageIndexDirectory;
|
property ImageIndexDirectory: integer read fImageIndexDirectory write fImageIndexDirectory;
|
||||||
property IdleConnected: boolean read fIdleConnected write SetIdleConnected;
|
property IdleConnected: Boolean read fIdleConnected write SetIdleConnected;
|
||||||
property SelectedPart: TObject read fSelectedPart write fSelectedPart;
|
property SelectedPart: TObject read fSelectedPart write fSelectedPart;
|
||||||
|
property SelectionList: TStringList read fSelectionList;
|
||||||
property ShowDirHierarchy: Boolean read fShowDirHierarchy write SetShowDirHierarchy;
|
property ShowDirHierarchy: Boolean read fShowDirHierarchy write SetShowDirHierarchy;
|
||||||
property SortData: Boolean read fSortData write fSortData;
|
property SortData: Boolean read fSortData write fSortData;
|
||||||
property Data: TStringList read fOriginalData;
|
property Data: TStringList read fOriginalData;
|
||||||
@ -163,11 +203,297 @@ begin
|
|||||||
RegisterComponents('LazControls',[TListFilterEdit]);
|
RegisterComponents('LazControls',[TListFilterEdit]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TFileNameItem }
|
||||||
|
|
||||||
constructor TFileNameItem.Create(AFilename: string);
|
constructor TFileNameItem.Create(AFilename: string);
|
||||||
begin
|
begin
|
||||||
Filename:=AFilename;
|
Filename:=AFilename;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TViewControl }
|
||||||
|
|
||||||
|
constructor TViewControl.Create(AOwner: TListFilterEdit);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
fOwner:=AOwner;
|
||||||
|
fImgIndex:=-1;
|
||||||
|
fSortedData:=TStringList.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TViewControl.Destroy;
|
||||||
|
begin
|
||||||
|
fSortedData.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TViewControl.CompareFNs(AFilename1,AFilename2: string): integer;
|
||||||
|
begin
|
||||||
|
if fOwner.fSortData then
|
||||||
|
Result:=CompareFilenames(AFilename1, AFilename2)
|
||||||
|
else if fOwner.fShowDirHierarchy then
|
||||||
|
Result:=CompareFilenames(ExtractFilePath(AFilename1), ExtractFilePath(AFilename2))
|
||||||
|
else
|
||||||
|
Result:=0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControl.SortAndFilter;
|
||||||
|
// Copy data from fOriginalData to fSortedData in sorted order
|
||||||
|
var
|
||||||
|
Origi, i: Integer;
|
||||||
|
FileN: string;
|
||||||
|
begin
|
||||||
|
fSortedData.Clear;
|
||||||
|
for Origi:=0 to fOwner.fOriginalData.Count-1 do begin
|
||||||
|
FileN:=fOwner.fOriginalData[Origi];
|
||||||
|
if (fOwner.Filter='') or (Pos(fOwner.Filter,lowercase(FileN))>0) then begin
|
||||||
|
i:=fSortedData.Count-1;
|
||||||
|
while i>=0 do begin
|
||||||
|
if CompareFNs(FileN,fSortedData[i])>=0 then break;
|
||||||
|
dec(i);
|
||||||
|
end;
|
||||||
|
fSortedData.InsertObject(i+1,FileN, fOwner.fOriginalData.Objects[Origi]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TViewControlTreeview }
|
||||||
|
|
||||||
|
constructor TViewControlTreeview.Create(AOwner: TListFilterEdit; aTreeview: TTreeview);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
fTreeview:=aTreeview;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TViewControlTreeview.Destroy;
|
||||||
|
begin
|
||||||
|
FreeTVNodeData(fOwner.fRootNode);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.ApplyFilter;
|
||||||
|
var
|
||||||
|
TVNode: TTreeNode;
|
||||||
|
i: Integer;
|
||||||
|
FileN, s: string;
|
||||||
|
ena: Boolean;
|
||||||
|
begin
|
||||||
|
with fOwner do begin
|
||||||
|
if fFilenameMap.Count > 0 then
|
||||||
|
FreeTVNodeData(fRootNode); // Free node data now, it will be filled later.
|
||||||
|
if Assigned(fRootNode) then // Delete old tree nodes.
|
||||||
|
fRootNode.DeleteChildren
|
||||||
|
else
|
||||||
|
fTreeview.Items.Clear;
|
||||||
|
if fShowDirHierarchy then
|
||||||
|
fTVNodeStack:=TFPList.Create;
|
||||||
|
fTreeview.BeginUpdate;
|
||||||
|
for i:=0 to fSortedData.Count-1 do begin
|
||||||
|
FileN:=fSortedData[i];
|
||||||
|
if fShowDirHierarchy then begin
|
||||||
|
TVClearUnneededAndCreateHierachy(FileN);
|
||||||
|
TVNode:=TTreeNode(fTVNodeStack[fTVNodeStack.Count-1]);
|
||||||
|
end
|
||||||
|
else if Assigned(fRootNode) then
|
||||||
|
TVNode:=fTreeview.Items.AddChild(fRootNode,FileN)
|
||||||
|
else
|
||||||
|
TVNode:=fTreeview.Items.Add(Nil,FileN);
|
||||||
|
if fFilenameMap.Count > 0 then begin
|
||||||
|
s:=FileN;
|
||||||
|
if fFilenameMap.Contains(FileN) then
|
||||||
|
s:=fFilenameMap[FileN]; // Full file name.
|
||||||
|
TVNode.Data:=TFileNameItem.Create(s);
|
||||||
|
end;
|
||||||
|
if Assigned(OnGetImageIndex) then
|
||||||
|
fImgIndex:=OnGetImageIndex(FileN, fSortedData.Objects[i], ena);
|
||||||
|
TVNode.ImageIndex:=fImgIndex;
|
||||||
|
TVNode.SelectedIndex:=fImgIndex;
|
||||||
|
if Assigned(fSelectedPart) then
|
||||||
|
TVNode.Selected:=fSelectedPart=fSortedData.Objects[i];
|
||||||
|
end;
|
||||||
|
if fShowDirHierarchy then // TVDeleteUnneededNodes(0); ?
|
||||||
|
fTVNodeStack.Free;
|
||||||
|
if Assigned(fRootNode) then
|
||||||
|
fRootNode.Expanded:=True;
|
||||||
|
fTreeview.EndUpdate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.StoreSelection;
|
||||||
|
var
|
||||||
|
ANode: TTreeNode;
|
||||||
|
begin
|
||||||
|
ANode:=fTreeview.Selected;
|
||||||
|
while ANode<>nil do begin
|
||||||
|
fOwner.fSelectionList.Insert(0,ANode.Text);
|
||||||
|
ANode:=ANode.Parent;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.RestoreSelection;
|
||||||
|
var
|
||||||
|
ANode: TTreeNode;
|
||||||
|
CurText: string;
|
||||||
|
begin
|
||||||
|
ANode:=nil;
|
||||||
|
with fOwner do begin
|
||||||
|
while fSelectionList.Count>0 do begin
|
||||||
|
CurText:=fSelectionList[0];
|
||||||
|
if ANode=nil then
|
||||||
|
ANode:=fTreeview.Items.GetFirstNode
|
||||||
|
else
|
||||||
|
ANode:=ANode.GetFirstChild;
|
||||||
|
while (ANode<>nil) and (ANode.Text<>CurText) do
|
||||||
|
ANode:=ANode.GetNextSibling;
|
||||||
|
if ANode=nil then break;
|
||||||
|
fSelectionList.Delete(0);
|
||||||
|
end;
|
||||||
|
if ANode<>nil then
|
||||||
|
fTreeview.Selected:=ANode;
|
||||||
|
fSelectionList.Clear;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.FreeTVNodeData(Node: TTreeNode);
|
||||||
|
var
|
||||||
|
Child: TTreeNode;
|
||||||
|
begin
|
||||||
|
if Node=nil then exit;
|
||||||
|
if (Node.Data<>nil) then begin
|
||||||
|
TObject(Node.Data).Free;
|
||||||
|
Node.Data:=nil;
|
||||||
|
end;
|
||||||
|
Child:=Node.GetFirstChild;
|
||||||
|
while Child<>nil do
|
||||||
|
begin
|
||||||
|
FreeTVNodeData(Child);
|
||||||
|
Child:=Child.GetNextSibling;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.TVDeleteUnneededNodes(p: integer);
|
||||||
|
// delete all nodes behind the nodes in the stack, and depth>=p
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
Node: TTreeNode;
|
||||||
|
begin
|
||||||
|
for i:=fTVNodeStack.Count-1 downto p do begin
|
||||||
|
Node:=TTreeNode(fTVNodeStack[i]);
|
||||||
|
while Node.GetNextSibling<>nil do
|
||||||
|
Node.GetNextSibling.Free;
|
||||||
|
end;
|
||||||
|
fTVNodeStack.Count:=p;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlTreeview.TVClearUnneededAndCreateHierachy(Filename: string);
|
||||||
|
// TVNodeStack contains a path of TTreeNode for the last filename
|
||||||
|
var
|
||||||
|
DelimPos: Integer;
|
||||||
|
FilePart: String;
|
||||||
|
Node: TTreeNode;
|
||||||
|
p: Integer;
|
||||||
|
begin
|
||||||
|
p:=0;
|
||||||
|
while Filename<>'' do begin
|
||||||
|
// get the next file name part
|
||||||
|
DelimPos:=System.Pos(PathDelim,Filename);
|
||||||
|
if DelimPos>0 then begin
|
||||||
|
FilePart:=copy(Filename,1,DelimPos-1);
|
||||||
|
Filename:=copy(Filename,DelimPos+1,length(Filename));
|
||||||
|
end else begin
|
||||||
|
FilePart:=Filename;
|
||||||
|
Filename:='';
|
||||||
|
end;
|
||||||
|
//debugln(['ClearUnneededAndCreateHierachy FilePart=',FilePart,' Filename=',Filename,' p=',p]);
|
||||||
|
if p < fTVNodeStack.Count then begin
|
||||||
|
Node:=TTreeNode(fTVNodeStack[p]);
|
||||||
|
if (FilePart=Node.Text) and (Node.Data=nil) then begin
|
||||||
|
// same sub directory
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// change directory => last directory is complete
|
||||||
|
// => delete unneeded nodes after last path
|
||||||
|
TVDeleteUnneededNodes(p+1);
|
||||||
|
if Node.GetNextSibling<>nil then begin
|
||||||
|
Node:=Node.GetNextSibling;
|
||||||
|
Node.Text:=FilePart;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Node:=fTreeview.Items.Add(Node,FilePart);
|
||||||
|
fTVNodeStack[p]:=Node;
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
// new sub node
|
||||||
|
if p>0 then
|
||||||
|
Node:=TTreeNode(fTVNodeStack[p-1])
|
||||||
|
else
|
||||||
|
Node:=fOwner.fRootNode;
|
||||||
|
if Node.GetFirstChild<>nil then begin
|
||||||
|
Node:=Node.GetFirstChild;
|
||||||
|
Node.Text:=FilePart;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Node:=fTreeview.Items.AddChild(Node,FilePart);
|
||||||
|
fTVNodeStack.Add(Node);
|
||||||
|
end;
|
||||||
|
if (Filename<>'') then begin
|
||||||
|
Node.ImageIndex:=fOwner.fImageIndexDirectory;
|
||||||
|
Node.SelectedIndex:=Node.ImageIndex;
|
||||||
|
end;
|
||||||
|
inc(p);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TViewControlListbox }
|
||||||
|
|
||||||
|
constructor TViewControlListbox.Create(AOwner: TListFilterEdit; aListbox: TListbox);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
fListbox:=aListbox;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TViewControlListbox.Destroy;
|
||||||
|
begin
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlListbox.ApplyFilter;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
FileN: string;
|
||||||
|
begin
|
||||||
|
fListbox.Clear;
|
||||||
|
fListbox.Items.BeginUpdate;
|
||||||
|
with fOwner do
|
||||||
|
for i:=0 to fSortedData.Count-1 do begin
|
||||||
|
FileN:=fSortedData[i];
|
||||||
|
fListbox.Items.AddObject(FileN, fSortedData.Objects[i]);
|
||||||
|
if Assigned(fSelectedPart) then
|
||||||
|
fListbox.Selected[i]:=fSelectedPart=fSortedData.Objects[i];
|
||||||
|
end;
|
||||||
|
fListbox.Items.EndUpdate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlListbox.StoreSelection;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to fListbox.Count-1 do begin
|
||||||
|
if fListbox.Selected[i] then
|
||||||
|
fOwner.fSelectionList.Add(fListbox.Items[i]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TViewControlListbox.RestoreSelection;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to fListbox.Count-1 do begin
|
||||||
|
if fOwner.fSelectionList.IndexOf(fListbox.Items[i])>0 then
|
||||||
|
fListbox.Selected[i]:=True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TListBoxFilterEdit }
|
{ TListBoxFilterEdit }
|
||||||
|
|
||||||
@ -175,7 +501,6 @@ constructor TListFilterEdit.Create(AOwner: TComponent);
|
|||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
fOriginalData:=TStringList.Create;
|
fOriginalData:=TStringList.Create;
|
||||||
fSortedData:=TStringList.Create;
|
|
||||||
fSelectionList:=TStringList.Create;
|
fSelectionList:=TStringList.Create;
|
||||||
fFilenameMap:=TStringToStringTree.Create(True);
|
fFilenameMap:=TStringToStringTree.Create(True);
|
||||||
fImageIndexDirectory := -1;
|
fImageIndexDirectory := -1;
|
||||||
@ -185,15 +510,15 @@ begin
|
|||||||
OnChange:=@FilterEditChange;
|
OnChange:=@FilterEditChange;
|
||||||
OnEnter:=@FilterEditEnter;
|
OnEnter:=@FilterEditEnter;
|
||||||
OnExit:=@FilterEditExit;
|
OnExit:=@FilterEditExit;
|
||||||
|
fIsFirstUpdate:=True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TListFilterEdit.Destroy;
|
destructor TListFilterEdit.Destroy;
|
||||||
begin
|
begin
|
||||||
FreeTVNodeData(fRootNode);
|
|
||||||
fFilenameMap.Free;
|
fFilenameMap.Free;
|
||||||
fSelectionList.Free;
|
fSelectionList.Free;
|
||||||
fSortedData.Free;
|
|
||||||
fOriginalData.Free;
|
fOriginalData.Free;
|
||||||
|
fViewControlWrapper.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -206,9 +531,11 @@ begin
|
|||||||
if Index < 0 then Exit;
|
if Index < 0 then Exit;
|
||||||
ena:=True;
|
ena:=True;
|
||||||
ImgIndex:=-1;
|
ImgIndex:=-1;
|
||||||
if Assigned(fImages4Listbox) and Assigned(OnGetImageIndex) then
|
if Assigned(fImages4Listbox) and Assigned(OnGetImageIndex) then begin
|
||||||
|
ena:=True;
|
||||||
ImgIndex:=OnGetImageIndex(fFilteredListbox.Items[Index],
|
ImgIndex:=OnGetImageIndex(fFilteredListbox.Items[Index],
|
||||||
fFilteredListbox.Items.Objects[Index], ena);
|
fFilteredListbox.Items.Objects[Index], ena);
|
||||||
|
end;
|
||||||
fFilteredListbox.Canvas.FillRect(ARect);
|
fFilteredListbox.Canvas.FillRect(ARect);
|
||||||
if ImgIndex<>-1 then
|
if ImgIndex<>-1 then
|
||||||
begin
|
begin
|
||||||
@ -294,6 +621,9 @@ begin
|
|||||||
if Assigned(fFilteredListbox) then
|
if Assigned(fFilteredListbox) then
|
||||||
raise Exception.Create('Sorry, both Treeview and ListBox should not be assigned.');
|
raise Exception.Create('Sorry, both Treeview and ListBox should not be assigned.');
|
||||||
fFilteredTreeview:=AValue;
|
fFilteredTreeview:=AValue;
|
||||||
|
if Assigned(fViewControlWrapper) then
|
||||||
|
fViewControlWrapper.Free;
|
||||||
|
fViewControlWrapper:=TViewControlTreeview.Create(Self, fFilteredTreeview);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TListFilterEdit.SetFilteredListbox(const AValue: TListBox);
|
procedure TListFilterEdit.SetFilteredListbox(const AValue: TListBox);
|
||||||
@ -301,15 +631,15 @@ begin
|
|||||||
if Assigned(fFilteredTreeview) then
|
if Assigned(fFilteredTreeview) then
|
||||||
raise Exception.Create('Sorry, both Treeview and ListBox should not be assigned.');
|
raise Exception.Create('Sorry, both Treeview and ListBox should not be assigned.');
|
||||||
fFilteredListbox:=AValue;
|
fFilteredListbox:=AValue;
|
||||||
if fFilteredListbox.Style=lbStandard then begin
|
if fFilteredListbox.Style<>lbStandard then begin
|
||||||
// ToDo: support normal drawing (lbStandard).
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
if Assigned(fFilteredListbox.OnDrawItem) then
|
if Assigned(fFilteredListbox.OnDrawItem) then
|
||||||
raise Exception.Create('Listbox.OnDrawItem should not be defined.'+
|
raise Exception.Create('Listbox.OnDrawItem should not be defined.'+
|
||||||
' ListFilterEdit assigns its own handler.');
|
' ListFilterEdit assigns its own handler.');
|
||||||
fFilteredListbox.OnDrawItem:=@ListBoxDrawItem;
|
fFilteredListbox.OnDrawItem:=@ListBoxDrawItem;
|
||||||
end;
|
end;
|
||||||
|
if Assigned(fViewControlWrapper) then
|
||||||
|
fViewControlWrapper.Free;
|
||||||
|
fViewControlWrapper:=TViewControlListbox.Create(Self, fFilteredListbox);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TListFilterEdit.SetShowDirHierarchy(const AValue: Boolean);
|
procedure TListFilterEdit.SetShowDirHierarchy(const AValue: Boolean);
|
||||||
@ -325,262 +655,21 @@ begin
|
|||||||
fFilenameMap[ShortFilename]:=FullFilename;
|
fFilenameMap[ShortFilename]:=FullFilename;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TListFilterEdit.StoreTreeSelection;
|
|
||||||
var
|
|
||||||
ANode: TTreeNode;
|
|
||||||
begin
|
|
||||||
ANode:=fFilteredTreeview.Selected;
|
|
||||||
while ANode<>nil do begin
|
|
||||||
fSelectionList.Insert(0,ANode.Text);
|
|
||||||
ANode:=ANode.Parent;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.StoreListSelection;
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
for i := 0 to fFilteredListbox.Count-1 do begin
|
|
||||||
if fFilteredListbox.Selected[i] then
|
|
||||||
fSelectionList.Add(fFilteredListbox.Items[i]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.RestoreTreeSelection;
|
|
||||||
var
|
|
||||||
ANode: TTreeNode;
|
|
||||||
CurText: string;
|
|
||||||
begin
|
|
||||||
ANode:=nil;
|
|
||||||
while fSelectionList.Count>0 do begin
|
|
||||||
CurText:=fSelectionList[0];
|
|
||||||
if ANode=nil then
|
|
||||||
ANode:=fFilteredTreeview.Items.GetFirstNode
|
|
||||||
else
|
|
||||||
ANode:=ANode.GetFirstChild;
|
|
||||||
while (ANode<>nil) and (ANode.Text<>CurText) do
|
|
||||||
ANode:=ANode.GetNextSibling;
|
|
||||||
if ANode=nil then break;
|
|
||||||
fSelectionList.Delete(0);
|
|
||||||
end;
|
|
||||||
if ANode<>nil then
|
|
||||||
fFilteredTreeview.Selected:=ANode;
|
|
||||||
fSelectionList.Clear;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.RestoreListSelection;
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
for i := 0 to fFilteredListbox.Count-1 do begin
|
|
||||||
if fSelectionList.IndexOf(fFilteredListbox.Items[i])>0 then
|
|
||||||
fFilteredListbox.Selected[i]:=True;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.FreeTVNodeData(Node: TTreeNode);
|
|
||||||
var
|
|
||||||
Child: TTreeNode;
|
|
||||||
begin
|
|
||||||
if Node=nil then exit;
|
|
||||||
if (Node.Data<>nil) then begin
|
|
||||||
TObject(Node.Data).Free;
|
|
||||||
Node.Data:=nil;
|
|
||||||
end;
|
|
||||||
Child:=Node.GetFirstChild;
|
|
||||||
while Child<>nil do
|
|
||||||
begin
|
|
||||||
FreeTVNodeData(Child);
|
|
||||||
Child:=Child.GetNextSibling;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.TVDeleteUnneededNodes(p: integer);
|
|
||||||
// delete all nodes behind the nodes in the stack, and depth>=p
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
Node: TTreeNode;
|
|
||||||
begin
|
|
||||||
for i:=fTVNodeStack.Count-1 downto p do begin
|
|
||||||
Node:=TTreeNode(fTVNodeStack[i]);
|
|
||||||
while Node.GetNextSibling<>nil do
|
|
||||||
Node.GetNextSibling.Free;
|
|
||||||
end;
|
|
||||||
fTVNodeStack.Count:=p;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.TVClearUnneededAndCreateHierachy(Filename: string);
|
|
||||||
// TVNodeStack contains a path of TTreeNode for the last filename
|
|
||||||
var
|
|
||||||
DelimPos: Integer;
|
|
||||||
FilePart: String;
|
|
||||||
Node: TTreeNode;
|
|
||||||
p: Integer;
|
|
||||||
begin
|
|
||||||
p:=0;
|
|
||||||
while Filename<>'' do begin
|
|
||||||
// get the next file name part
|
|
||||||
DelimPos:=System.Pos(PathDelim,Filename);
|
|
||||||
if DelimPos>0 then begin
|
|
||||||
FilePart:=copy(Filename,1,DelimPos-1);
|
|
||||||
Filename:=copy(Filename,DelimPos+1,length(Filename));
|
|
||||||
end else begin
|
|
||||||
FilePart:=Filename;
|
|
||||||
Filename:='';
|
|
||||||
end;
|
|
||||||
//debugln(['ClearUnneededAndCreateHierachy FilePart=',FilePart,' Filename=',Filename,' p=',p]);
|
|
||||||
if p < fTVNodeStack.Count then begin
|
|
||||||
Node:=TTreeNode(fTVNodeStack[p]);
|
|
||||||
if (FilePart=Node.Text) and (Node.Data=nil) then begin
|
|
||||||
// same sub directory
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
// change directory => last directory is complete
|
|
||||||
// => delete unneeded nodes after last path
|
|
||||||
TVDeleteUnneededNodes(p+1);
|
|
||||||
if Node.GetNextSibling<>nil then begin
|
|
||||||
Node:=Node.GetNextSibling;
|
|
||||||
Node.Text:=FilePart;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Node:=fFilteredTreeview.Items.Add(Node,FilePart);
|
|
||||||
fTVNodeStack[p]:=Node;
|
|
||||||
end;
|
|
||||||
end else begin
|
|
||||||
// new sub node
|
|
||||||
if p>0 then
|
|
||||||
Node:=TTreeNode(fTVNodeStack[p-1])
|
|
||||||
else
|
|
||||||
Node:=fRootNode;
|
|
||||||
if Node.GetFirstChild<>nil then begin
|
|
||||||
Node:=Node.GetFirstChild;
|
|
||||||
Node.Text:=FilePart;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Node:=fFilteredTreeview.Items.AddChild(Node,FilePart);
|
|
||||||
fTVNodeStack.Add(Node);
|
|
||||||
end;
|
|
||||||
if (Filename<>'') then begin
|
|
||||||
Node.ImageIndex:=fImageIndexDirectory;
|
|
||||||
Node.SelectedIndex:=Node.ImageIndex;
|
|
||||||
end;
|
|
||||||
inc(p);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TListFilterEdit.CompareFNs(AFilename1,AFilename2: string): integer;
|
|
||||||
begin
|
|
||||||
if fSortData then
|
|
||||||
Result:=CompareFilenames(AFilename1, AFilename2)
|
|
||||||
else if fShowDirHierarchy then
|
|
||||||
Result:=CompareFilenames(ExtractFilePath(AFilename1), ExtractFilePath(AFilename2))
|
|
||||||
else
|
|
||||||
Result:=0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.SortAndFilter;
|
|
||||||
// Copy data from fOriginalData to fSortedData in sorted order
|
|
||||||
var
|
|
||||||
Origi, i: Integer;
|
|
||||||
FileN: string;
|
|
||||||
begin
|
|
||||||
fSortedData.Clear;
|
|
||||||
for Origi:=0 to fOriginalData.Count-1 do begin
|
|
||||||
FileN:=fOriginalData[Origi];
|
|
||||||
if (Filter='') or (Pos(Filter,lowercase(FileN))>0) then begin
|
|
||||||
i:=fSortedData.Count-1;
|
|
||||||
while i>=0 do begin
|
|
||||||
if CompareFNs(FileN,fSortedData[i])>=0 then break;
|
|
||||||
dec(i);
|
|
||||||
end;
|
|
||||||
fSortedData.InsertObject(i+1,FileN, fOriginalData.Objects[Origi]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.ApplyFilterToTreeview;
|
|
||||||
var
|
|
||||||
TVNode: TTreeNode;
|
|
||||||
ImgIndex, i: Integer;
|
|
||||||
FileN, s: string;
|
|
||||||
ena: Boolean;
|
|
||||||
begin
|
|
||||||
ImgIndex:=-1;
|
|
||||||
if fSelectedPart=Nil then
|
|
||||||
StoreTreeSelection;
|
|
||||||
if fFilenameMap.Count > 0 then
|
|
||||||
FreeTVNodeData(fRootNode); // Free node data now, it will be filled later.
|
|
||||||
if Assigned(fRootNode) then // Delete old tree nodes.
|
|
||||||
fRootNode.DeleteChildren
|
|
||||||
else
|
|
||||||
fFilteredTreeview.Items.Clear;
|
|
||||||
if fShowDirHierarchy then
|
|
||||||
fTVNodeStack:=TFPList.Create;
|
|
||||||
fFilteredTreeview.BeginUpdate;
|
|
||||||
for i:=0 to fSortedData.Count-1 do begin
|
|
||||||
FileN:=fSortedData[i];
|
|
||||||
if fShowDirHierarchy then begin
|
|
||||||
TVClearUnneededAndCreateHierachy(FileN);
|
|
||||||
TVNode:=TTreeNode(fTVNodeStack[fTVNodeStack.Count-1]);
|
|
||||||
end
|
|
||||||
else if Assigned(fRootNode) then
|
|
||||||
TVNode:=fFilteredTreeview.Items.AddChild(fRootNode,FileN)
|
|
||||||
else
|
|
||||||
TVNode:=fFilteredTreeview.Items.Add(Nil,FileN);
|
|
||||||
if fFilenameMap.Count > 0 then begin
|
|
||||||
s:=FileN;
|
|
||||||
if fFilenameMap.Contains(FileN) then
|
|
||||||
s:=fFilenameMap[FileN]; // Full file name.
|
|
||||||
TVNode.Data:=TFileNameItem.Create(s);
|
|
||||||
end;
|
|
||||||
if Assigned(OnGetImageIndex) then
|
|
||||||
ImgIndex:=OnGetImageIndex(FileN, fSortedData.Objects[i], ena);
|
|
||||||
TVNode.ImageIndex:=ImgIndex;
|
|
||||||
TVNode.SelectedIndex:=ImgIndex;
|
|
||||||
if Assigned(fSelectedPart) then
|
|
||||||
TVNode.Selected:=fSelectedPart=fSortedData.Objects[i];
|
|
||||||
end;
|
|
||||||
if fShowDirHierarchy then // TVDeleteUnneededNodes(0); ?
|
|
||||||
fTVNodeStack.Free;
|
|
||||||
if Assigned(fRootNode) then
|
|
||||||
fRootNode.Expanded:=True;
|
|
||||||
if fSelectedPart=Nil then
|
|
||||||
RestoreTreeSelection;
|
|
||||||
fFilteredTreeview.EndUpdate;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.ApplyFilterToListBox;
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
FileN: string;
|
|
||||||
begin
|
|
||||||
if fSelectedPart=Nil then
|
|
||||||
StoreListSelection;
|
|
||||||
fFilteredListbox.Clear;
|
|
||||||
fFilteredListbox.Items.BeginUpdate;
|
|
||||||
for i:=0 to fSortedData.Count-1 do begin
|
|
||||||
FileN:=fSortedData[i];
|
|
||||||
fFilteredListbox.Items.AddObject(FileN, fSortedData.Objects[i]);
|
|
||||||
if Assigned(fSelectedPart) then
|
|
||||||
fFilteredListbox.Selected[i]:=fSelectedPart=fSortedData.Objects[i];
|
|
||||||
end;
|
|
||||||
if fSelectedPart=Nil then
|
|
||||||
RestoreListSelection;
|
|
||||||
fFilteredListbox.Items.EndUpdate;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TListFilterEdit.ApplyFilter(Immediately: Boolean);
|
procedure TListFilterEdit.ApplyFilter(Immediately: Boolean);
|
||||||
begin
|
begin
|
||||||
if Immediately then begin
|
if Immediately then begin
|
||||||
fNeedUpdate:=false;
|
if not (Assigned(fFilteredTreeview) or Assigned(fFilteredListbox)) then
|
||||||
SortAndFilter;
|
|
||||||
if Assigned(fFilteredTreeview) then
|
|
||||||
ApplyFilterToTreeview
|
|
||||||
else if Assigned(fFilteredListbox) then
|
|
||||||
ApplyFilterToListBox
|
|
||||||
else
|
|
||||||
raise Exception.Create(
|
raise Exception.Create(
|
||||||
'Set either FilteredTreeview or FilteredListbox before using the filter.');
|
'Set either FilteredTreeview or FilteredListbox before using the filter.');
|
||||||
|
fNeedUpdate:=false;
|
||||||
|
fViewControlWrapper.SortAndFilter;
|
||||||
|
if (fSelectedPart=Nil) and not fIsFirstUpdate then
|
||||||
|
fViewControlWrapper.StoreSelection; // At first round the selection is from caller
|
||||||
|
if fIsFirstUpdate then
|
||||||
|
fIsFirstUpdate:=False;
|
||||||
|
fViewControlWrapper.ApplyFilter;
|
||||||
|
fSelectedPart:=Nil;
|
||||||
|
fViewControlWrapper.RestoreSelection;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
if csDestroying in ComponentState then exit;
|
if csDestroying in ComponentState then exit;
|
||||||
|
Loading…
Reference in New Issue
Block a user