Mega Code Archive

 
Categories / Delphi / Files
 

DirectoryTree Component

Title: DirectoryTree Component Question: This component acts and looks like the window to the left of the vertical splitter of Windows Explorer. Answer: {--------------------------------------------------------------------- DirectoryTree (Visual Component) ---------------------------------------------------------------------- This component acts and looks like the window, to the left of the vertical splitter of Windows Explorer. With this visual component you are able to choose a drive and / or a directory and react on the OnChange event. If the OnChange event occurs, you can read the new directory in the property: Directory of the component. The component even works correct at design time, although you will not be able to open the drives during design time. For suggestions, improvements, remarks, enhancements, please email: M.deHaan@inn.nl This component is tested under Windows 95A (SP1) and Windows NT (4.0 SP6) and it is written in Delphi 5.0. The component will be registered under 'Samples', but you can change that. ---------------------------------------------------------------------- ================= November 17, 2000 ================= Under WinNT the directories of this component are always sorted. Under W95 the directories of this component are NOT sorted. I have tried to get the directories sorted with the property "AlphaSort" set to true, while creating a new node, but this doesn't seem work for ALL nodes. All root directories still remain unsorted. A work around for this inconveniance is shown in the procedure FINDDIRS. The disadvantage of this method is that it takes just a little bit longer befor the directorie entries are shown in the component... ================ January 10, 2002 ================ By means of first reading the directories in a TStringList and then, when all the directories are read, read them into the TreeView, the sorting problem is completely solved! (You can set the sorted property of the TStringList to false or true, so the TreeView will be sorted or not.) ================ January 12, 2002 ================ Changed the behaviour of the DirectoryTree component. 1) If you click once on a directory name, it will be selected. 2) If you click once on the '+'-button or '-'-button of a node, it will collapse or implode. Depending on the previous state and depending if it can collapse. If it cannot collapse (when there are no subdirectories) the '+'-button disappears. Clicking on the '+'-button or '-'-button of a node doesn't cause the selected directory to change, unless a subdirectory of this node was selected and the node is imploded by clicking on the '+'-button. 3) Double clicking on the icon or the directory name is the same as under 2. With these modifications, the DirectoryTree acts more like the directorytree to the left of the vertical splitter in the Windows Explorer. (I hope you'll agree to that..) ---------------------------------------------------------------------} Unit DirectoryTree; // {$I+,Q+,S+,R+,H+,X+} Interface Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, ImgList, StdCtrls, FileCtrl; Const // You can change this into your own language, if you want Rootname : String = 'My Computer'; Type TDirectoryTree = class(TCustomTreeView) private { Private declarations } fStringList : TStringList; // added 10-01-02 fImageList : TCustomImageList; fDirectory : String; fOnChange : TNotifyEvent; // fDirLabelSet : Boolean; // removed 21-01-02 fTreenodes : TTreenodes; fCurDrive : String; fSort : Boolean; // added 17-11-00 Procedure FindDirs(S : String; T : TTreenode); // Procedure GetNodeInfo(T : TTreenode); // removed 23-01-02 Procedure fChanges; dynamic; Procedure fSetSort(Value : Boolean); Protected { Protected declarations } // Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; // X, Y: Integer); override; // removed 12-01-02 Procedure Click; override; // added 12-01-02 Procedure DblClick; override; // added 12-01-02 Public { Public declarations } Constructor Create(AOwner : TComponent); override; Destructor Destroy; override; // Procedure UpDate; reintroduce; // removed 12-01-02 Procedure FindDrives; dynamic; Procedure CreateWnd; override; Published { Published declarations } {--- properties ---} Property Align; Property Anchors; Property Color; Property Constraints; Property Cursor; Property Directory : String read fDirectory write fDirectory; Property DragCursor; Property DragKind; Property DragMode; Property Enabled; Property Font; Property Height; Property HelpContext; Property Hint; Property Left; Property Name; Property ParentColor; Property ParentFont; Property ParentShowHint; Property PopupMenu; Property ShowHint; Property Sort : Boolean read fSort write fSetSort; // added 17-11-00 Property TabOrder; Property TabStop; Property Tag; Property Top; Property Visible; Property Width; {--- Events ---} Property OnChange : TNotifyEvent read fOnChange write fOnChange; Property OnClick; Property OnDblClick; Property OnDragDrop; Property OnDragOver; Property OnEndDrag; Property OnEnter; Property OnExit; Property OnKeyDown; Property OnKeyPress; Property OnKeyUp; Property OnMouseDown; Property OnMouseMove; Property OnMouseUp; Property OnStartDrag; End; Procedure Register; // Load the bitmaps, 16 x 16 bits, 256 color {$R IMAGES.RES} Implementation {--------------------------------------------------------------------} Function IsDriveReady(Const Ch : Char) : Boolean; Var SR : TSearchRec; oldErrorMode : Integer; Begin oldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); {$I-} If FindFirst(Ch + ':\*.*',faAnyfile,SR) = 0 then Result := True else Result := False; FindClose(SR); SetErrorMode(oldErrorMode); {$I+} End; {--------------------------------------------------------------------} (* From Delphi 5 sources: c:\program files\borland\delphi5\source\vcl\filectrl.pas Procedure TDirectoryTree.SetFileListBox(Value: TFileListBox); Begin If fFileList nil then fFileList.DirList := nil; fFileList := Value; If fFileList nil then Begin fFileList.DirList := Self; fFileList.FreeNotification(Self); End; End; *) {--------------------------------------------------------------------} (* From Delphi 5 sources: c:\program files\borland\delphi5\source\vcl\filectrl.pas Procedure CutFirstDirectory(var S: TFileName); Var Root : Boolean; P : Integer; Begin If S = '\' then S := '' else Begin If S[1] = '\' then Begin Root := True; Delete(S, 1, 1); End else Root := False; If S[1] = '.' then Delete(S, 1, 4); P := AnsiPos('\',S); If P 0 then Begin Delete(S, 1, P); S := '...\' + S; End else S := ''; If Root then S := '\' + S; End; End; *) {--------------------------------------------------------------------} (* From Delphi 5 sources: c:\program files\borland\delphi5\source\vcl\filectrl.pas Function MinimizeName(const Filename: TFileName; Canvas: TCanvas; MaxLen: Integer): TFileName; Var Drive : TFileName; Dir : TFileName; Name : TFileName; Begin Result := FileName; Dir := ExtractFilePath(Result); Name := ExtractFileName(Result); If (Length(Dir) = 2) and (Dir[2] = ':') then begin Drive := Copy(Dir, 1, 2); Delete(Dir, 1, 2); end else Drive := ''; While ((Dir '') or (Drive '')) and (Canvas.TextWidth(Result) MaxLen) do Begin If Dir = '\...\' then Begin Drive := ''; Dir := '...\'; End else If Dir = '' then Drive := '' else CutFirstDirectory(Dir); Result := Drive + Dir + Name; End; End; *) {--------------------------------------------------------------------} (* From Delphi 5 sources: c:\program files\borland\delphi5\source\vcl\filectrl.pas Procedure TDirectoryTree.SetDirLabel (Value: TLabel); Begin fDirLabel := Value; if Value nil then Value.FreeNotification(Self); SetDirLabelCaption; End; *) {--------------------------------------------------------------------} (* From Delphi: c:\program files\borland\delphi5\source\vcl\filectrl.pas Procedure TDirectoryTree.SetDirLabelCaption; Var DirWidth: Integer; Begin If fDirLabel nil then Begin DirWidth := Width; If not fDirLabel.AutoSize then DirWidth := fDirLabel.Width; fDirLabel.Caption := MinimizeName(Directory, fDirLabel.Canvas, DirWidth); End; End; *) {--------------------------------------------------------------------} Procedure TDirectoryTree.fSetSort(Value : Boolean); // added 17-11-00 Begin If fSort Value then Begin fSort := Value; Invalidate; End; End; {--------------------------------------------------------------------} Procedure TDirectoryTree.fChanges; Begin If Assigned(fOnChange) then fOnChange(Self); End; {--------------------------------------------------------------------} // Procedure FindDirs is changed on: 10-01-02 // The dirs found are first loaded in a TStringList of which the // property "Sorted" can be set to "True" or "False". // Then, when all directories are found, the TCustomTreeView is updated, // thus achieving a sorted or a non-sorted TreeView. {--------------------------------------------------------------------} Procedure TDirectoryTree.FindDirs(S : String; T : TTreenode); Var SR : TSearchRec; T1 : TTreenode; S1 : String; I : Byte; Begin S1 := S; If S[Length(S)] '\' then S1 := S1 + '\'; If not IsDriveReady(S1[1]) then Exit; // Throw away the old entries fStringList.Clear; // added 10-01-02 // Sorted or not fStringList.Sorted := fSort; // added 10-01-02 // Sorting cannot be undone, see Delphi's help on "TStringList.Sorted" // changed 10-01-02 If FindFirst(S1 + '*.*',faAnyFile,SR) = 0 then // changed 12-01-02 Begin // changed and simplified 12-01-02 If ((SR.Attr and faDirectory) = faDirectory) then If (SR.Name '.') and (SR.Name '..') then fStringList.Add(SR.Name); While FindNext(SR) = 0 do Begin If ((SR.Attr and faDirectory) = faDirectory) then If (SR.Name '.') and (SR.Name '..') then fStringList.Add(SR.Name); End; End; FindClose(SR); // added 19-01-02 // This peace is added 10-01-02 // Update the TreeView from the StringList, thus solving the sorting // problem If fStringList.Count 0 then Begin fTreeNodes.BeginUpdate; // added 12-01-02 For I := 0 to fStringList.Count-1 do Begin T1 := Items.AddChild(T,fStringList.Strings[I]); T1.SelectedIndex := 1; T1.HasChildren := True; End; fTreeNodes.EndUpdate; // added 12-01-02 End; End; {--------------------------------------------------------------------} (* Procedure TDirectoryTree.GetNodeInfo(T : TTreenode); Var S : String; T1 : TTreenode; Begin S := T.Text; If S = Rootname then Exit; T1 := T; While Pos(':',S) 2 do Begin T1 := T1.Parent; S := T1.Text + '\' + S; End; If T.Count = 0 then FindDirs(S,T); If fDirectory S then Begin fDirectory := S; fChanges; End; End; *) {--------------------------------------------------------------------} Procedure TDirectoryTree.FindDrives; Var Tr,T1 : TTreenode; ld : DWord; I : Integer; Drive : String; Begin Items.Clear; ld := GetLogicalDrives; Tr := Items.Add(nil,Rootname); Tr.ImageIndex := 2; Tr.SelectedIndex := 2; fTreeNodes.BeginUpdate; // added 22-01-02 For I := 0 to 25 do Begin If (ld and (1 shl I)) 0 then Begin Drive := Chr(65 + I) + ':'; T1 := Items.AddChild(Tr,Drive); T1.HasChildren := True; // Create a '+' in the node // Adjust drive icon Case GetDriveType(PChar(Drive[1] + ':\')) of 0,DRIVE_FIXED : Begin T1.ImageIndex := 3; T1.SelectedIndex := 3; End; DRIVE_CDROM : Begin T1.ImageIndex := 4; T1.SelectedIndex := 4; End; DRIVE_REMOVABLE : Begin T1.ImageIndex := 5; T1.SelectedIndex := 5; End; DRIVE_RAMDISK: Begin T1.ImageIndex := 6; T1.SelectedIndex := 6; End; DRIVE_REMOTE : Begin T1.ImageIndex := 7; T1.SelectedIndex := 7; End; End; // of Case If fCurDrive = Drive then T1.Selected := True; // Select current drive End; End; fTreeNodes.EndUpdate; // added 22-01-02 End; {--------------------------------------------------------------------} Constructor TDirectoryTree.Create(AOwner : TComponent); Var dBitmap : TBitmap; Begin inherited Create(AOwner); // Init CustomTreeview ShowRoot := True; ShowButtons := True; ReadOnly := True; // Init Sort Sort := True; // added 17-11-00 fSort := True; // fDirLabelSet := False; // removed 21-01-02 fDirectory := ''; fImageList := TCustomImageList.Create(Self); fImageList.Clear; fImageList.BkColor := clWhite; fImageList.BlendColor := clWhite; fImageList.Masked := True; fImageList.Height := 16; fImageList.Width := 16; fImageList.AllocBy := 7; // Load DIRCLOSE bitmap dBitmap := TBitmap.Create; // create dummy bitmap dBitmap.Handle := LoadBitmap(hInstance,'DIRCLOSE'); // Add to ImageList fImageList.Add(dBitmap,nil); // 0 // Load DIROPEN bitmap dBitmap.Handle := LoadBitmap(hInstance,'DIROPEN'); // Add to ImageList fImageList.Add(dBitmap,nil); // 1 // Load COMPUTER bitmap dBitmap.Handle := LoadBitmap(hInstance,'COMPUTER'); // Add to ImageList fImageList.Add(dBitmap,nil); // 2 // Load HARDDISK bitmap dBitmap.Handle := LoadBitmap(hInstance,'HARDDISK'); // Add to ImageList fImageList.Add(dBitmap,nil); // 3 // Load CDROMDISK bitmap dBitmap.Handle := LoadBitmap(hInstance,'CDROMDISK'); // The word 'CDROM' gives // problems (reserved?) // Add to ImageList fImageList.Add(dBitmap,nil); // 4 // Load FLOPPYDISK bitmap dBitmap.Handle := LoadBitmap(hInstance,'FLOPPYDISK'); // A bitmap named 'FLOPPY' // already exists // (reserved?) // Add to ImageList fImageList.Add(dBitmap,nil); // 5 // Load RAMDISK bitmap dBitmap.Handle := LoadBitmap(hInstance,'RAMDISK'); // Add to ImageList fImageList.Add(dBitmap,nil); // 6 // Load REMOTEDISK bitmap dBitmap.Handle := LoadBitmap(hInstance,'REMOTEDISK'); // Add to ImageList fImageList.Add(dBitmap,nil); // 7 // Free the dummy bitmap dBitmap.Free; // Assign the imagelist to TreeView.Images Images := fImageList; // The CustomTreeView has no treenodes yet, so we have to create // them... fTreenodes := TTreenodes.Create(Self); fTreenodes.Clear; // Clear the treenodes fStringList := TStringList.Create; // added 10-01-02 End; {--------------------------------------------------------------------} Procedure TDirectoryTree.CreateWnd; Var P : String; Begin inherited CreateWnd; GetDir(0,P); fCurDrive := UpCase(P[1]) + ':'; FindDrives; // is dynamic!! End; {--------------------------------------------------------------------} // Added 12-01-02 Procedure TDirectoryTree.DblClick; Var T,T1 : TTreenode; S : String; HT : THitTests; iOldCount : Integer; pPoint : TPoint; Begin inherited DblClick; GetCursorPos(pPoint); // Get cursor position pPoint := ScreenToClient(pPoint); // Translate to client coordinates HT := GetHitTestInfoAt(pPoint.X,pPoint.Y); // Check for hits // Handle the DblClick on an item of a node If (htOnItem in HT) then Begin T := GetNodeAt(pPoint.X,pPoint.Y); // Highlight the name T.Selected := True; // Save old count iOldCount := T.Count; S := T.Text; If S = Rootname then Exit; // Get full path T1 := T; While Pos(':',S) 2 do Begin T1 := T1.Parent; S := T1.Text + '\' + S; End; // Is the path changed? If fDirectory S then Begin fDirectory := S; fChanges; End; // Find the directories (if any) If T.Count = 0 then FindDirs(S,T); // Only the first time it will not expand by clicking on the button If T.Count = 0 then T.HasChildren := False // Removes the '+'-button else Begin If (iOldCount = 0) then T.Expanded := True; End; End; End; {--------------------------------------------------------------------} // Added 12-01-02 Procedure TDirectoryTree.Click; Var T,T1 : TTreenode; S : String; HT : THitTests; iOldCount : Integer; pPoint : TPoint; Begin inherited Click; GetCursorPos(pPoint); // Get cursor position pPoint := ScreenToClient(pPoint); // Translate to client coordinates HT := GetHitTestInfoAt(pPoint.X,pPoint.Y); // Check for hits // Handle the Click on the '+'-button or '-'-button of a node If (htOnButton in HT) then Begin T := GetNodeAt(pPoint.X,pPoint.Y); // Save old count iOldCount := T.Count; S := T.Text; If S = Rootname then Exit; // Get full path T1 := T; While Pos(':',S) 2 do Begin T1 := T1.Parent; S := T1.Text + '\' + S; End; // Find the direectories (if any) If T.Count = 0 then FindDirs(S,T); // Only the first time it will not expand by clicking on the button If T.Count = 0 then T.HasChildren := False // Removes the '+'-button else Begin If (iOldCount = 0) then T.Expanded := True; End; End; //Handle the Click on an item of a node If (htOnItem in HT) then Begin T := GetNodeAt(pPoint.X,pPoint.Y); S := T.Text; If S = Rootname then Exit; // Get full path T1 := T; While Pos(':',S) 2 do Begin T1 := T1.Parent; S := T1.Text + '\' + S; End; // Changed? If fDirectory S then Begin fDirectory := S; fChanges; End; // Highlight the name T.Selected := True; End; End; {--------------------------------------------------------------------} (* Procedure TDirectoryTree.Update; Var P : String; Begin GetDir(0,P); fCurDrive := UpCase(P[1]) + ':'; ChDir(fCurDrive); FindDrives; fChanges; End; *) {--------------------------------------------------------------------} Destructor TDirectoryTree.Destroy; Begin fImageList.Free; // Free the ImageList fTreenodes.Free; // Free the Treenodes fStringList.Free; // Free the StringList, added 10-01-02 inherited Destroy; End; {--------------------------------------------------------------------} Procedure Register; Begin RegisterComponents('Samples', [TDirectoryTree]); End; {--------------------------------------------------------------------} End. {====================================================================}