mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 22:29:37 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit frmFileBrowser;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, FileUtil, LResources, LCLType, Forms, Controls, Graphics,
 | |
|   Dialogs, EditBtn, FileCtrl, ComCtrls, StdCtrls, ExtCtrls;
 | |
| 
 | |
| type
 | |
|   TOpenFileEvent = procedure(Sender: TObject; const AFileName: string) of object;
 | |
| 
 | |
|   { TFileBrowserForm }
 | |
| 
 | |
|   TFileBrowserForm = class(TForm)
 | |
|     btnConfigure: TButton;
 | |
|     btnReload: TButton;
 | |
|     cbHidden: TCheckBox;
 | |
|     FileListBox: TFileListBox;
 | |
|     FilterComboBox: TFilterComboBox;
 | |
|     Panel1: TPanel;
 | |
|     Splitter1: TSplitter;
 | |
|     TV: TTreeView;
 | |
|     procedure btnConfigureClick(Sender: TObject);
 | |
|     procedure btnReloadClick(Sender: TObject);
 | |
|     procedure cbHiddenChange(Sender: TObject);
 | |
|     procedure FileListBoxDblClick(Sender: TObject);
 | |
|     procedure FilterComboBoxChange(Sender: TObject);
 | |
|     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
 | |
|     procedure FormCreate(Sender: TObject);
 | |
|     procedure FormShow(Sender: TObject);
 | |
|     procedure TVExpanded(Sender: TObject; Node: TTreeNode);
 | |
|     procedure TVSelectionChanged(Sender: TObject);
 | |
|     procedure FormActivate(Sender: TObject);
 | |
|     procedure FilterComboBoxSelect(Sender: TObject);
 | |
|     procedure FileListBoxKeyPress(Sender: TObject; var Key: char);
 | |
|   private
 | |
|     FOnConfigure: TNotifyEvent;
 | |
|     FOnOpenFile: TOpenFileEvent;
 | |
|     FOnSelectDir: TNotifyEvent;
 | |
|     FRootDir: string;
 | |
|     FDir: string;
 | |
|     FShowHidden: Boolean;
 | |
|     procedure AddDirectories(Node: TTreeNode; Dir: string);
 | |
|     function GetAbsolutePath(Node: TTreeNode): string;
 | |
|     procedure SetDir(const Value: string);
 | |
|     procedure SetRootDir(const Value: string);
 | |
|     procedure InitializeTreeview;
 | |
|     {$IFDEF MSWINDOWS}
 | |
|     procedure AddWindowsDriveLetters;
 | |
|     {$ENDIF}
 | |
|   public
 | |
|     { return the selected directory }
 | |
|     function SelectedDir: string;
 | |
|     { The selected/opened directory }
 | |
|     property Directory: string read FDir write SetDir;
 | |
|     { Directory the treeview starts from }
 | |
|     property RootDirectory: string read FRootDir write SetRootDir;
 | |
|     { Must we show hidden directories - not working on unix type systems }
 | |
|     property ShowHidden: Boolean read FShowHidden write FShowHidden default False;
 | |
|     { Called when user double-clicks file name }
 | |
|     property OnOpenFile: TOpenFileEvent read FOnOpenFile write FOnOpenFile;
 | |
|     { Called when user clicks configure button }
 | |
|     property OnConfigure: TNotifyEvent read FOnConfigure write FOnConfigure;
 | |
|     { Called when a new directory is selected }
 | |
|     property OnSelectDir: TNotifyEvent read FOnSelectDir write FOnSelectDir;
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   FileBrowserForm: TFileBrowserForm;
 | |
| 
 | |
| 
 | |
| implementation
 | |
| 
 | |
| {$R frmfilebrowser.lfm}
 | |
| 
 | |
| {$IFDEF MSWINDOWS}
 | |
| uses
 | |
|   Windows;
 | |
| {$ENDIF}
 | |
| 
 | |
| const
 | |
|   cFilter = 'All Files (' + AllFilesMask + ')|' + AllFilesMask +
 | |
|             '|Source(*.pas;*.pp)|*.pas;*.pp' +
 | |
|             '|Projectfiles(*.pas;*.pp;*.inc;*.lfm;*.lpr;*.lrs;*.lpi;*.lpk)|' +
 | |
|             '*.pas;*.pp;*.inc;*.lfm;*.lpr;*.lrs;*.lpi;*.lpk;|';
 | |
| 
 | |
| 
 | |
| {function HasSubDirs returns True if the directory passed has subdirectories}
 | |
| function HasSubDirs(const Dir: string; AShowHidden: Boolean): Boolean;
 | |
| var
 | |
|   FileInfo: TSearchRec;
 | |
|   FCurrentDir: string;
 | |
| begin
 | |
|   //Assume No
 | |
|   Result := False;
 | |
|   if Dir <> '' then
 | |
|   begin
 | |
|     FCurrentDir := AppendPathDelim(Dir);
 | |
|     FCurrentDir := FCurrentDir + GetAllFilesMask;
 | |
|     try
 | |
|       if SysUtils.FindFirst(FCurrentDir, faAnyFile or $00000080, FileInfo) = 0 then
 | |
|         repeat
 | |
|           if FileInfo.Name = '' then
 | |
|             Continue;
 | |
| 
 | |
|             // check if special file
 | |
|           if ((FileInfo.Name = '.') or (FileInfo.Name = '..')) or
 | |
|             // unix dot directories (aka hidden directories)
 | |
|             ((FileInfo.Name[1] in ['.']) and AShowHidden) or
 | |
|             // check Hidden attribute
 | |
|             (((faHidden and FileInfo.Attr) > 0) and AShowHidden) then
 | |
|             Continue;
 | |
| 
 | |
|           Result := ((faDirectory and FileInfo.Attr) > 0);
 | |
| 
 | |
|           //We found at least one non special dir, that's all we need.
 | |
|           if Result then
 | |
|             break;
 | |
|         until SysUtils.FindNext(FileInfo) <> 0;
 | |
|     finally
 | |
|       SysUtils.FindClose(FileInfo);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TFileBrowserForm }
 | |
| 
 | |
| procedure TFileBrowserForm.TVExpanded(Sender: TObject; Node: TTreeNode);
 | |
| begin
 | |
|   if Node.Count = 0 then
 | |
|     AddDirectories(Node, GetAbsolutePath(Node));
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.TVSelectionChanged(Sender: TObject);
 | |
| begin
 | |
|   FileListBox.Directory := ChompPathDelim(SelectedDir);
 | |
|   if Assigned(OnSelectDir) then
 | |
|     OnselectDir(Self);
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FormActivate(Sender: TObject);
 | |
| begin
 | |
|   { for some reason this does not work in FormShow }
 | |
|   TV.MakeSelectionVisible;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FilterComboBoxSelect(Sender: TObject);
 | |
| begin
 | |
|   FileListBox.Mask := FilterComboBox.Mask;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FileListBoxKeyPress(Sender: TObject; var Key: char);
 | |
| begin
 | |
|   if Key = Char(VK_RETURN) then
 | |
|    FileListBoxDblClick(Sender);
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.btnConfigureClick(Sender: TObject);
 | |
| begin
 | |
|   if Assigned(FOnConfigure) then
 | |
|     FOnConfigure(Self);
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.btnReloadClick(Sender: TObject);
 | |
| var
 | |
|   d: string;
 | |
| begin
 | |
|   // save current directory location
 | |
|   d := ChompPathDelim(SelectedDir);
 | |
|   // rebuild tree
 | |
|   TV.Items.Clear;
 | |
|   InitializeTreeview;
 | |
|   // restore directory
 | |
|   Directory := d;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.cbHiddenChange(Sender: TObject);
 | |
| begin
 | |
|   ShowHidden := cbHidden.Checked;
 | |
|   if ShowHidden then
 | |
|     FileListBox.FileType := FileListBox.FileType + [ftHidden]
 | |
|   else
 | |
|     FileListBox.FileType := FileListBox.FileType - [ftHidden];
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FileListBoxDblClick(Sender: TObject);
 | |
| begin
 | |
|   if Assigned(FOnOpenFile) then
 | |
|     FOnOpenFile(Self, FileListBox.FileName);
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FilterComboBoxChange(Sender: TObject);
 | |
| begin
 | |
|   FileListBox.Mask := FilterComboBox.Text;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 | |
| begin
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FormCreate(Sender: TObject);
 | |
| begin
 | |
|   FShowHidden := False;
 | |
|   InitializeTreeview;
 | |
|   FilterComboBox.Filter := cFilter;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.FormShow(Sender: TObject);
 | |
| begin
 | |
|   if TV.Selected <> nil then
 | |
|     TV.Selected.Expand(False);
 | |
| end;
 | |
| 
 | |
| { Adds Subdirectories to a passed node if they exist }
 | |
| procedure TFileBrowserForm.AddDirectories(Node: TTreeNode; Dir: string);
 | |
| var
 | |
|   FileInfo: TSearchRec;
 | |
|   NewNode: TTreeNode;
 | |
|   i: integer;
 | |
|   FCurrentDir: string;
 | |
|   //used to sort the directories.
 | |
|   SortList: TStringList;
 | |
| begin
 | |
|   if Dir <> '' then
 | |
|   begin
 | |
|     FCurrentDir := Dir;
 | |
|     FCurrentDir := AppendPathDelim(FCurrentDir);
 | |
|     i           := length(FCurrentDir);
 | |
|     FCurrentDir := FCurrentDir + GetAllFilesMask;
 | |
|     try
 | |
|       if SysUtils.FindFirst(FCurrentDir, faAnyFile or $00000080, FileInfo) = 0 then
 | |
|       begin
 | |
|         try
 | |
|           SortList        := TStringList.Create;
 | |
|           SortList.Sorted := True;
 | |
|           repeat
 | |
|             // check if special file
 | |
|             if (FileInfo.Name = '.') or (FileInfo.Name = '..') or (FileInfo.Name = '') then
 | |
|               Continue;
 | |
|             // if hidden files or directories must be filtered, we test for
 | |
|             // dot files, considered hidden under unix type OS's.
 | |
|             if not FShowHidden then
 | |
|               if (FileInfo.Name[1] in ['.']) then
 | |
|                 Continue;
 | |
| 
 | |
|             // if this is a directory then add it to the tree.
 | |
|             if ((faDirectory and FileInfo.Attr) > 0) then
 | |
|             begin
 | |
|               //if this is a hidden file and we have not been requested to show
 | |
|               //hidden files then do not add it to the list.
 | |
|               if ((faHidden and FileInfo.Attr) > 0) and not FShowHidden then
 | |
|                 continue;
 | |
| 
 | |
|               SortList.Add(FileInfo.Name);
 | |
|             end;
 | |
|           until SysUtils.FindNext(FileInfo) <> 0;
 | |
|           for i := 0 to SortList.Count - 1 do
 | |
|           begin
 | |
|             NewNode := TV.Items.AddChild(Node, SortList[i]);
 | |
|             // if subdirectories then indicate so.
 | |
|             NewNode.HasChildren := HasSubDirs(AppendPathDelim(Dir) + NewNode.Text, FShowHidden);
 | |
|           end;
 | |
|         finally
 | |
|           SortList.Free;
 | |
|         end;
 | |
|       end;  { if FindFirst... }
 | |
|     finally
 | |
|       SysUtils.FindClose(FileInfo);
 | |
|     end;
 | |
|   end;  { if Dir... }
 | |
|   if Node.Level = 0 then
 | |
|     Node.Text := Dir;
 | |
| end;
 | |
| 
 | |
| function TFileBrowserForm.GetAbsolutePath(Node: TTreeNode): string;
 | |
| begin
 | |
|   Result := '';
 | |
|   while Node <> nil do
 | |
|   begin
 | |
|     if Node.Text = PathDelim then
 | |
|       Result := Node.Text + Result
 | |
|     else
 | |
|       Result := Node.Text + PathDelim + Result;
 | |
|     Node := Node.Parent;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.SetDir(const Value: string);
 | |
| var
 | |
|   StartDir: string;
 | |
|   Node: TTreeNode;
 | |
|   i, p: integer;
 | |
|   SubDir: PChar;
 | |
| begin
 | |
|   FDir     := Value;
 | |
|   StartDir := Value;
 | |
|   if TV.Items.Count = 0 then
 | |
|     Exit;
 | |
|   p := AnsiPos(FRootDir, StartDir);
 | |
|   if p = 1 then
 | |
|     Delete(StartDir, P, Length(FRootDir));
 | |
|   for i := 1 to Length(StartDir) do
 | |
|     if (StartDir[i] = PathDelim) then
 | |
|       StartDir[i] := #0;
 | |
|   SubDir := PChar(StartDir);
 | |
|   if SubDir[0] = #0 then
 | |
|     SubDir := @SubDir[1];
 | |
|   Node := TV.Items.GetFirstNode;
 | |
|   while SubDir[0] <> #0 do
 | |
|   begin
 | |
|     Node := Node.GetFirstChild;
 | |
|     while (Node <> nil) and (AnsiCompareStr(Node.Text, SubDir) <> 0) do
 | |
|       Node := Node.GetNextSibling;
 | |
|     if Node = nil then
 | |
|       break
 | |
|     else
 | |
|       Node.Expand(False);
 | |
|     SubDir := SubDir + StrLen(SubDir) + 1;
 | |
|   end;
 | |
|   TV.Selected := Node;
 | |
|   TV.MakeSelectionVisible;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.SetRootDir(const Value: string);
 | |
| var
 | |
|   RootNode: TTreeNode;
 | |
|   lNode: TTreeNode;
 | |
| begin
 | |
|   { Clear the list }
 | |
|   TV.Items.Clear;
 | |
|   FRootDir := Value;
 | |
| 
 | |
|   {$IFDEF MSWINDOWS}
 | |
|   { Add Windows drive letters }
 | |
|   AddWindowsDriveLetters;
 | |
|   {$ENDIF}
 | |
| 
 | |
|   { Remove the path delimiter unless this is root. }
 | |
|   if FRootDir = '' then
 | |
|     FRootDir := PathDelim;
 | |
|   if (FRootDir <> PathDelim) and (FRootDir[length(FRootDir)] = PathDelim) then
 | |
|     FRootDir := copy(FRootDir, 1, length(FRootDir) - 1);
 | |
|   { Find or Create the root node and add it to the Tree View. }
 | |
|   RootNode := TV.Items.FindTopLvlNode(FRootDir + PathDelim);
 | |
|   if RootNode = nil then
 | |
|     RootNode := TV.Items.Add(nil, FRootDir);
 | |
| 
 | |
|   { Add the Subdirectories to Root nodes }
 | |
|   lNode := TV.Items.GetFirstNode;
 | |
|   while lNode <> nil do
 | |
|   begin
 | |
|     AddDirectories(lNode, lNode.Text);
 | |
|     lNode := lNode.GetNextSibling;
 | |
|   end;
 | |
| 
 | |
|   { Set the original root node as the selected node. }
 | |
|   TV.Selected := RootNode;
 | |
| end;
 | |
| 
 | |
| procedure TFileBrowserForm.InitializeTreeview;
 | |
| begin
 | |
|   { I'm not sure what we should set these to. Maybe another Config option? }
 | |
|   {$IFDEF UNIX}
 | |
|   RootDirectory := '/';
 | |
|   {$ENDIF}
 | |
|   {$IFDEF MSWINDOWS}
 | |
|   RootDirectory := 'C:\';
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| {$IFDEF MSWINDOWS}
 | |
| procedure TFileBrowserForm.AddWindowsDriveLetters;
 | |
| const
 | |
|   MAX_DRIVES = 25;
 | |
| var
 | |
|   n: integer;
 | |
|   drvs: string;
 | |
| begin
 | |
|   // making drive list, skipping drives A: and B: and Removable Devices without media
 | |
|   n := 2;
 | |
|   while n <= MAX_DRIVES do
 | |
|   begin
 | |
|     drvs := chr(n + Ord('A')) + ':\';
 | |
|     if (Windows.GetDriveType(PChar(drvs)) <> 1) and
 | |
|     (GetDiskFreeSpaceEx(PChar(drvs), nil, nil, nil)) then
 | |
|       TV.Items.Add(nil, drvs);
 | |
|     Inc(n);
 | |
|   end;
 | |
| end;
 | |
| {$ENDIF}
 | |
| 
 | |
| function TFileBrowserForm.SelectedDir: string;
 | |
| begin
 | |
|   Result := '';
 | |
|   if TV.Selected <> nil then
 | |
|     Result := GetAbsolutePath(TV.Selected);
 | |
| end;
 | |
| 
 | |
| end.
 | |
| 
 | 
