Felipe Monteiro de Carvalho wrote 20 lines
Hello,
I am trying to implement TShellTreeView. It is a treeview which shows
the directory structure in the prefered way of the operating system.
In Windows that would start with My Computer, etc...
One problem now is with the design. Should it read all existing
directories (which will cause a delay) or only those visible, or one
or two levels at once... what do you think? Reading everything may be
problematic with removable media.
Another problem is using TTreeView. I searched by I can't find how I
would implement this behavior, for example, having the root and 1
level visible and then when the user clicks the + symbol the subitems
for this item are loaded. Should I use the OnExpanding event? In the
event handler I would create the subitems? A tutorial would be
great...
thanks,
Well, I know it is utter crap as far as performance is concerned (and design and many other things :) ), but you could take a look at the code below (I had posted it on the forum too):
{
/*************************************************************************** TDirectoryTreeview.pas
----------
Component For Displaying A Tree Of Directories In A FileSystem ----------
by George "cyberpython" Migdos
***************************************************************************/
***************************************************************************** *
*
*
* See the Lazarus Distribution
COPYING.modifiedLGPL, *
* for details about the
copyright. * *
*
* This code is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. *
*
*
***************************************************************************** }
unit DirectoryTreeview;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, ComCtrls, Dialogs;
type
{ TDirectoryTreeview }
TDirectoryTreeview = class(TTreeview)
private
{ Private declarations }
FRootDirs: TStrings;
FShowHidden : Boolean;
FShowSelf : Boolean;
FShowParent : Boolean;
FFolderImageIndex : Integer;
FFolderOpenImageIndex : Integer;
FFolderSelectedImageIndex : Integer;
FFolderOpenSelectedImageIndex : Integer;
FRootDirectories : TStrings;
procedure SetRootDirs(Value : TStrings);
procedure SetFolderImageIndex(const NewIndex: Integer);
procedure SetFolderOpenImageIndex(const NewIndex: Integer);
procedure SetFolderSelectedImageIndex(const NewIndex: Integer);
procedure SetFolderOpenSelectedImageIndex(const NewIndex: Integer);
function searchForFolders(ParentFolder : String; showHidden,
includeSelf, includeParent : boolean) : TStringList;
function getNodePath(Node : TTreeNode) : String;
function getChildNodeByData(NodeParent : TTreeNode; data :
Pointer):TTreeNode;
procedure populateTreeView(root : TTreeNode; CurrentDepth, MaxDepth
: LongInt);
protected
{ Protected declarations }
procedure DoPaintNode(Node: TTreeNode);override;
public
{ Public declarations }
constructor Create(AnOwner: TComponent);override;
destructor Destroy;override;
function getPath : String;
published
{ Published declarations }
property RootDirectories: TStrings read FRootDirs write SetRootDirs;
property ShowHidden: Boolean read FShowHidden write FShowHidden;
property ShowSelf: Boolean read FShowSelf write FShowSelf;
property ShowParent: Boolean read FShowParent write FShowParent;
property FolderImageIndex : Integer read FFolderImageIndex write
SetFolderImageIndex;
property FolderOpenImageIndex : Integer read FFolderOpenImageIndex
write SetFolderOpenImageIndex;
property FolderSelectedImageIndex : Integer read
FFolderSelectedImageIndex write SetFolderSelectedImageIndex;
property FolderOpenSelectedImageIndex : Integer read
FFolderOpenSelectedImageIndex write SetFolderOpenSelectedImageIndex;
end;
procedure Register;
implementation
procedure removeBlankLinesFromTheBottom(Strings : TStrings);
var
i : integer;
reachedLast: boolean;
begin
reachedLast := false;
i:=Strings.Count-1;
while( (i>=0)AND(not reachedLast) )do
begin
if(Strings.Strings[i]='')then
begin
Strings.Delete(i);
end
else
reachedLast:=true;
i:=i-1;
end;
end;
function TDirectoryTreeview.searchForFolders(ParentFolder : String;
showHidden, includeSelf, includeParent : boolean) : TStringList;
var
searchResult : TSearchRec;
res : TStringList;
begin
res:=TStringList.Create;
if(directoryExists(ParentFolder))then
begin
if FindFirst(ParentFolder+PathDelim+'*', faAnyFile, searchResult) = 0
then
begin
repeat
if ( (searchResult.Attr AND faDirectory) > 0 ) then
begin
if ( searchResult.Name = '.') then
begin
if(includeSelf)then
res.Add(searchResult.Name);
end
else if (searchResult.Name = '..')then
begin
if(includeParent)then
res.Add(searchResult.Name);
end
else
begin
if(length(searchResult.Name) > 0)then
begin
{$ifdef Win32}
if ( not showHidden )then
begin
if ( (searchResult.Attr AND faHidden) <= 0 )then
begin
res.Add(searchResult.Name);
end;
end
else
begin
res.Add(searchResult.Name);
end;
{$endif}
{$ifdef Unix}
if(not showHidden)then
begin
if(searchResult.Name[1] '.')then
begin
res.Add(searchResult.Name);
end;
end
else
begin
res.Add(searchResult.Name);
end;
{$endif}
end;
end;
end;
until FindNext(searchResult) 0;
FindClose(searchResult);
end;
end;
result:=res;
end;
function TDirectoryTreeview.getNodePath(Node : TTreeNode) : String;
var
rootDir : String;
n : TTreeNode;
begin
n:=Node;
rootDir:= PChar(n.Data);
while (n.Parent nil)do
begin
n:=n.Parent;
if( pChar(n.Data) PathDelim)then
rootDir:=PChar(n.Data)+PathDelim+rootDir
else
rootDir:=PChar(n.Data)+rootDir;
end;
result:=rootDir;
end;
function TDirectoryTreeview.getChildNodeByData(NodeParent : TTreeNode;
data : Pointer): TTreeNode;
var
n : TTreeNode;
found : boolean;
d : String;
res : TTreeNode;
begin
res:=nil;
n:=NodeParent.GetFirstChild;
found := false;
d := PChar(data);
while( (n nil) and (found=false) )do
begin
if(PChar(n.Data) = d)then
begin
found:=true;
res:=n;
end;
n:=n.GetNextSibling;
end;
result:=res;
end;
procedure TDirectoryTreeview.populateTreeView(root : TTreeNode;
CurrentDepth, MaxDepth : LongInt);
var
i: integer;
res: TStringList;
dirname : string;
n, ch, tmpNode : TTreeNode;
rootDir : String;
begin
rootDir := getNodePath(root);
if(directoryExists(rootDir))then
begin
if(CurrentDepth=0)then
Items.BeginUpdate;
res := searchForFolders(rootDir, FShowHidden, FShowSelf, FShowParent);
//remove the nodes that represent deleted directories
//
tmpNode:=root.GetFirstChild;
while(tmpNode nil)do
begin
n:=tmpNode;
tmpNode:=tmpNode.GetNextSibling;
if(res.IndexOf(pChar(n.Data)) < 0 )then
begin
n.Delete;
end;
end;
//
for i:= 0 to res.Count-1 do
begin
dirname:= res.Strings[i];
if(length(dirname) > 0)then
begin
tmpNode:= getChildNodeByData(root, PChar(dirname));
if( tmpNode = nil )then
begin
n:=Items.AddChild(root, res.Strings[i]);
n.Data:=pChar(res.Strings[i]);
n.ImageIndex:=self.FFolderImageIndex;
n.SelectedIndex:=self.FFolderSelectedImageIndex;
end;
end;
end;
if(CurrentDepth < MaxDepth)then
begin
if(root.Count>0)then
begin
ch:= root.GetFirstChild;
while(ch nil)do
begin
populateTreeView(ch, CurrentDepth+1, MaxDepth);
ch:=ch.GetNextSibling;
end;
end;
end;
root.AlphaSort;
if(CurrentDepth=0)then
Items.EndUpdate;
res.Free;
end;
end;
constructor TDirectoryTreeview.Create(AnOwner: TComponent);
begin
inherited;
FRootDirs := TStringList.Create;
FRootDirectories := TStringList.Create;
ShowHidden := false;
ShowSelf:=false;
ShowParent:=false;
FolderImageIndex := -1;
FolderOpenImageIndex := -1;
FolderSelectedImageIndex := -1;
FolderOpenSelectedImageIndex := -1;
end;
destructor TDirectoryTreeview.Destroy;
begin
FRootDirectories.Free;
FRootDirs.Free;
inherited;
end;
procedure TDirectoryTreeview.SetRootDirs(Value : TStrings);
var
i : integer;
n : TTreeNode;
tmp2 : TStringList;
imgIndex, SelImgIndex : integer;
begin
FRootDirs.Assign(Value);
removeBlankLinesFromTheBottom(FRootDirs);
FRootDirectories.Clear;
tmp2:=TStringList.Create;
tmp2.Delimiter:=',';
if((FRootDirs.Count mod 3)=0)then
begin
Items.Clear;
i:=0;
while(i< FRootDirs.Count-1) do
begin
tmp2.Clear;
tmp2.DelimitedText:=FRootDirs.Strings[i+2];
n:=Items.Add(nil,FRootDirs.Strings[i]);
FRootDirectories.Add(FRootDirs.Strings[i+1]);
n.Data:=pChar(FRootDirectories.Strings[FRootDirectories.Count-1]);
if(tmp2.Count = 2)then
begin
Try
imgIndex:=strtoint(tmp2.Strings[0]);
SelImgIndex:=strtoint(tmp2.Strings[1]);
except
on E : Exception do
begin
imgIndex:=self.FFolderImageIndex;
SelImgIndex:=self.FFolderSelectedImageIndex;
end;
end;
end
else
begin
imgIndex:=self.FFolderImageIndex;
SelImgIndex:=self.FFolderSelectedImageIndex;
end;
n.ImageIndex:=imgIndex;
n.SelectedIndex:=SelImgIndex;
populateTreeView(n, 0, 2);
i:=i+3;
end;
n:=Items.GetFirstNode;
if(n nil)then
begin
n.Expand(false);//remove this if you activate the while loop
while(n nil)do
begin
//n.Expand(false);
self.DoPaintNode(n);
n:= n.GetNextSibling;
end;
end;
end
else
showMessage('Invalid root directories!');
tmp2.Free;
self.DoPaint;
self.Repaint;
end;
function TDirectoryTreeview.getPath : String;
begin
if(Selected nil)then
result:= getNodePath(Selected);
end;
procedure TDirectoryTreeview.SetFolderImageIndex(const NewIndex: Integer);
begin
FFolderImageIndex := NewIndex;
end;
procedure TDirectoryTreeview.SetFolderOpenImageIndex(const NewIndex:
Integer);
begin
FFolderOpenImageIndex := NewIndex;
end;
procedure TDirectoryTreeview.SetFolderSelectedImageIndex(const NewIndex:
Integer);
begin
FFolderSelectedImageIndex := NewIndex;
end;
procedure TDirectoryTreeview.SetFolderOpenSelectedImageIndex(const
NewIndex: Integer);
begin
FFolderOpenSelectedImageIndex := NewIndex;
end;
procedure TDirectoryTreeview.DoPaintNode(Node: TTreeNode);
begin
populateTreeView(Node, 0, 2);
if(Node.Parentnil)then
begin
if(Node.Expanded)then
begin
Node.ImageIndex:=self.FFolderOpenImageIndex;
Node.SelectedIndex:=self.FFolderOpenSelectedImageIndex;
end
else
begin
Node.ImageIndex:= self.FFolderImageIndex;
Node.SelectedIndex:=self.FFolderSelectedImageIndex;
end;
end;
inherited;
end;
procedure Register;
begin
RegisterComponents('Misc', [TDirectoryTreeview]);
end;
end.