Recent

Author Topic: Component for generating file and directory include/exclude lists  (Read 1351 times)

colo

  • New Member
  • *
  • Posts: 46
Colleagues from work are looking for an easy to use way to browse through directory trees (could me multiple, starting from different drives/root directories) to (de)select elements from that are to be included and/or excluded in the resulting selection. They want to make their lives operating various CLI utils on different OS/platforms (rsync, robocopy, etc.) easier that way.

I've been thinking about the problem a bit, and what I think is required is an expandable display of directory hierarchies/trees with some sort of multi-value (include, inherit, exclude) checkbox in front of each individual element. Walking the tree(s) to emit "--include <elem>" and "--exclude <elem>" according to the target CLI util's rules and needs would then have to be implemented in application logic.

I was wondering if there's a component that would suit itself to such a job (the file/dir picking only), but the component library is so vast that I'm afraid I might overlook it... so if anyone has got a suggestion, please let me know! :)

cdbc

  • Hero Member
  • *****
  • Posts: 1645
    • http://www.cdbc.dk
Re: Component for generating file and directory include/exclude lists
« Reply #1 on: October 03, 2023, 10:57:19 am »
Hi
"TListFileSearcher" springs to mind... uses "FileUtil"
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Component for generating file and directory include/exclude lists
« Reply #2 on: October 03, 2023, 11:33:37 am »
Here is an example based on the visual TShellTreeView and TShellListView components:
  • Drop a TShellTreeView and a TShellListView on the form, link them via the ShellListview property of the treeview (or the ShellTreeView property of the listview)
  • Drop a TListbox for collecting the selected (checked) file names.
  • Activate the listview's checkboxes. There is an inherited property Checkboxes which is public only, i.e. does not appear in the object inspector. Set it to true by code in the form's OnCreate event handler.
  • Write a handler for the listview's OnItemChecked event. The handler should extract the full filename from the clicked listitem (ShellListView.GetPathFromItem(...)) and add or remove this string to the listbox. Note that OnItemChecked event is still protected in TShellListview, but can be made public by subclassing:
Code: Pascal  [Select][+][-]
  1. type
  2.   TShellListView = class(ShellCtrls.TShellListView)
  3.   public
  4.     property OnItemChecked;
  5.   end;
  6.  
  7. procedure TForm1.FormCreate(Sender: TObject);
  8. begin
  9.   ShellListView1.Checkboxes := true;
  10.   ShellListView1.OnItemChecked := @ShellListViewItemChecked;
  11. end;
  12.  
  13. procedure TForm1.ShellListViewItemChecked(Sender: TObject; Item: TListItem);
  14. var
  15.   fn: String;
  16.   idx: Integer;
  17. begin
  18.   fn := ShellListView1.GetPathFromItem(Item);
  19.   if Item.Checked then
  20.     Listbox1.Items.Add(fn)
  21.   else
  22.   begin
  23.     idx := Listbox1.Items.IndexOf(fn);
  24.     if idx > -1 then
  25.       Listbox1.Items.Delete(idx);
  26.   end;
  27. end;
  28.  
  • Write a handler for the OnFileAdded event of the ShellListview. It should set the checkmarks according to the already checked files in the listbox:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ShellListView1FileAdded(Sender: TObject; Item: TListItem);
  2. var
  3.   fn: String;
  4. begin
  5.   fn := ShellListView1.GetPathFromItem(Item);
  6.   Item.Checked := Listbox1.Items.IndexOf(fn) >= 0;
  7. end;
    « Last Edit: October 03, 2023, 11:41:11 am by wp »

    colo

    • New Member
    • *
    • Posts: 46
    Re: Component for generating file and directory include/exclude lists
    « Reply #3 on: October 03, 2023, 08:58:24 pm »
    Oh wow, this looks very close to what I had in mind already! Thanks so much for the input, I will see what I can do with that :)

    colo

    • New Member
    • *
    • Posts: 46
    Re: Component for generating file and directory include/exclude lists
    « Reply #4 on: October 04, 2023, 04:44:52 pm »
    So I have tried to expand a bit on your generously provided example (Using Lazarus 2.2.6 on GNU/Linux amd64 with Qt5, but I will eventually also need to compile this for mac OS and Windows), and have a number of questions that range from extremely to moderately embarassing ;) (I haven't done Lazarus/FP programming in a while, and the application uses controls I've had no experience with, and shows some behavior I am not familiar with at all, so please, bear with me! :)) - I realize I am in no way entitled to having these answered by anyone, and I will try to figure it all out by myself - but if there's a hint or answer to be had, I'd be very grateful to read it! Here goes:

    • How can I make the TShellListView items appear ordered by name whenever its current directory has changed/its contents were updated (i.e., what is the proper/optimal place to call ShellListView1.Sort in)?
    • How can I attach a custom doubleclick event handler to items in TShellListView1? I would like to have a procedure that evaluates
      the item (:=path in the filesystem) doubleclicked on, and if it happens to be a directory, navigate the associated TShellTreeView1 to that location in the FS. (My TShellListView1 is set to display [otFolders,otNonFolders,otHidden], so that users can also check and thereby add whole directories to the ListBox1 items.)
    • What do I need to do so that I can have new controls on TForm1 align themselves similar to how TShellTreeView1 and TShellListView1 do? I would like an additional TGroupBox with some controls inside eventually, but when I add that to Form1 using the form designer, the existing controls do not contract/shrink in order to make some room for it (and I am not sure which control and/or property causes this behavior, unfortunately).
    • How can I add or remove columns from the vsReport ViewStyle of ShellListView1? I would like to remove the "Type" column (displaying file extensions after the last "." in the entries' names) and add a column denoting whether or not the entry listed is a directory, normal file, special file, etc. Ideally, I would like to display a distinct graphical component (small icon) for each type - if that is even possible, while still maintaining the vsReport style?

    Thanks so much to anyone who chimes in! :)

    wp

    • Hero Member
    • *****
    • Posts: 12457
    Re: Component for generating file and directory include/exclude lists
    « Reply #5 on: October 04, 2023, 06:59:18 pm »
    How can I make the TShellListView items appear ordered by name whenever its current directory has changed/its contents were updated (i.e., what is the proper/optimal place to call ShellListView1.Sort in)?
    The TShellListView has a SortOrder property; setting it to stText sorts the items alphabetically (stData sort numerically by the Data pointer interpreted as numbers - not used by the standard TShellListView)

    How can I attach a custom doubleclick event handler to items in TShellListView1? I would like to have a procedure that evaluates
    the item (:=path in the filesystem) doubleclicked on, and if it happens to be a directory, navigate the associated TShellTreeView1 to that location in the FS.
    Select the ShellListView, and click on the '...' next to the OnDblClick event in the event tab of the object inspector. Enter the following code to the generated code OnDblClick skeleton.

    Code: Pascal  [Select][+][-]
    1. procedure TForm1.ShellListView1DblClick(Sender: TObject);
    2. var
    3.   item: TShellListItem;
    4. begin
    5.   item := TShellListItem(ShellListView1.Selected);
    6.   if item.IsFolder then
    7.     ShellTreeView1.Path := ShellListView1.GetPathFromItem(item);
    8. end;

    Explanation:
    Normally the items in a TListView are type TListItem. TShellListView implements a descedant TShellListItem which has an field FileInfo containing the stored TSearchRec for each found file system item. TShellListView, therefore, can provide a method IsFolder telling whether this particular item is a folder or not. Either from the FileInfo or, for simpler, from the ShellListView's GetPathFromItem method you can determine the full path of the specified item; the selected item is accessible as ShellListview.Selected. And finally we assign this path to the Path property of the ShellTreeView which makes it switch to this path, open its nodes and communicates with the associated ShellListview to show the files.

    What do I need to do so that I can have new controls on TForm1 align themselves similar to how TShellTreeView1 and TShellListView1 do? I would like an additional TGroupBox with some controls inside eventually, but when I add that to Form1 using the form designer, the existing controls do not contract/shrink in order to make some room for it (and I am not sure which control and/or property causes this behavior, unfortunately).
    I suggest you create a new project to play with the Align properties of all controls:
    • Drop a TPanel on the new form, set its Align to alLeft --> the panel will expand to cover the left part of the form.
    • Add another panel, set its Align to alClient --> this panel will cover the rest of the form. When you resize the form, both panels will nicely fill the form, the width of the left panel will not change.
    • If you want the user to be able to change the left panel width, add a splitter to the form. Since in the current state, however, the entire form is covered by panels, you very probably added the splitter to one of the panels rather than to the form: Go to the object tree above the object inspector, find the splitter's node and drag it immediately below the form's node. Now the splitter is a direct child of the form, but probably located at the very left of the form. Go to the form editor, select the splitter and drag it to the right of the left panel so that it snaps at its right side.
    • In the same way you can add a right-(or top- or bottom-)aligned panel with Align set to alRight (or alTop or alBottom, respectively).
    • And you can use other controls instead of the panels or place other controls into the panels where you again can use the Align property to position them.
    In my opinion this is a simple possibility to achieve a "nice" layout. Another, more advanced, possibility is the Anchor Editor - read about it in https://wiki.freepascal.org/Anchor_Sides.

    How can I add or remove columns from the vsReport ViewStyle of ShellListView1? I would like to remove the "Type" column (displaying file extensions after the last "." in the entries' names) and add a column denoting whether or not the entry listed is a directory, normal file, special file, etc. Ideally, I would like to display a distinct graphical component (small icon) for each type - if that is even possible, while still maintaining the vsReport style?
    Columns have not been in the focus of the ShellListView development... But you can add a new column after the ShellListview has been created, e.g. in the OnCreate event of the form:
    Code: Pascal  [Select][+][-]
    1. var
    2.   MyColIndex: Integer;
    3.  
    4. procedure TForm1.Formcreate(Sender: TObject);
    5. begin
    6.   ...
    7.   with ShellListView1.Columns.Add do
    8.   begin
    9.     MyColIndex := Index;  // Save the index of the column to access it later
    10.     Caption := 'Test';
    11.     Width := 80;
    12.     Alignment := taRightJustify;
    13.   end;

    I think, you cannot delete the default columns, but you can hide them:
    Code: Pascal  [Select][+][-]
    1. procedure TForm1.FormCreate(Sender: TObject);
    2. begin
    3.   ShellTreeView1.Columns[2].Visible := false;   // Column #2 is the "Type" column
    4.  

    The widths of the 3 default columns are maintained by the component, therefore, you may have to readjust column widths when you add or hide columns yourself.

    In Windows, shell tree and listview items automatically get icons from the system. In Linux and Mac you must do this yourself: Add a TImagelist component to the form, doubleclick on it and add the icons that you need. Assign this imageList to the Images property of the ShellTreeView and the SmallImages property of the ShellTreeView (you need LargeImages fom a second image list also when you switch the ShellListView to ViewStyle vsIcon). Then, in the OnFileAdded event of the ShellListView, assign the requested image by its indes in the ImageList to the ImageIndex property of the provided Item. In the same way there is a OnGetImageIndex property for the ShellTreeView (and use the OnGetSelectedIndex to assign the image index to be used when a tree node is selected).

    colo

    • New Member
    • *
    • Posts: 46
    Re: Component for generating file and directory include/exclude lists
    « Reply #6 on: October 05, 2023, 08:42:19 am »
    Oh wow, thanks so much for this detailed and super helpful reply! I will try to follow along this week, and hope to be able to create something useful in the process  O:-)

     

    TinyPortal © 2005-2018