Recent

Author Topic: [SOLVED] Treeview, possible to late set Node.HasChildren (plus sign)?  (Read 20124 times)

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
I need to do a correcton in one point
(wrong assumption): as far as duplicate calls to SHGetFileInfoW are not done along all subfolders of the test folders (18000 in the test case(, but only for those that are part to be painted at once (let's say 30 as an example), it should NOT harm the performance.
So a dedicated event slot within PainNode called separated from OnGetImageIndex, would make sense too.
I tested this by adding a second dummy SHGetFileInfoW call within OnGetImageIndex and had no noticeable impact. I assume the test could be representative.
Sorry!
Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
Node.HasChildren is used at many locations in the CustomTreeView (DoCanExpand, InternalMove, DeleteChildren, GetInternalMarkAt). Since in your solution HasChildren is valid only after painting, what if an undrawn node is processed and its HasChildren is requested (e.g. expand c:\windows\winsxs when only c:\ is open -- this will not work...!)?
I added a TShellTreeView to a form and a button to open c:\windows\winsxs in the tree:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   ShellTreeView1.Path := 'C:\Windows\WinSxS';
  4. end;
When I run this program the requested folder is opened in the treeview (after some time, of course...).

Now I tried to simulate your optimization in the case when the node to be opened has not yet been painted (because this is where the node gets the HasChildren value in your optimization):
In shellctrls I commented the lines in TCustomShellTreeView.PopulateTreeNodeWithFiles which assign a value of HasChildren to the NewNode. So, the nodes do not yet know HasChildren upon creation.
Then, in TCustomTreeView.DoPaintNode, I added "Node.HasChildren" at the top - this is a simplification, but corresponds to what you want to do: Assign the HasChildren value somewhere in this procedure.

When I run the program now and click the button, the ShellTreeView opens only the folder C:\Windows - it cannot open its WinSxS subfolder because the nodes have not yet been painted.

Therefore, I am convinced that your idea must be decoupled from the painting cycle. What I try to implement next is as follows:
- Add a new NodeState, nsValidHasChildren
- When a new node is created the nsValidHasChildren is not yet included in the NodeStates, i.e. it is unknown whether the node has children.
- The setter TTreeNode.SetHasChildren addes nsValidHasChildren to the NodeStates - now it is known whether the node has children.
- The getter TTreeNode.GetHasChildren checks whether nsvalidHasChildren already has been added -- in this case it return the usual value. If it has not yet been added it causes the treeview to call a virtual funtion NodeHasChildren which fires an event OnHasChildren. The ShellTreeview overrides this method to scans the file system and looks for children. The result is stored as nsHasChildren in the NodeStates, and the nsValidHasChildren is added to signal that the HasChildren value is known now as well.

This architecture avoids setting the HasChildren value at creation of the nodes. But via the method/event the true value can determined at the first time when it is needed.

I am attaching preliminarily patched version of the related units. The side cases with hidden and non-folder objects are not yet checked.
Copy the files over the original ones (make a backup copy first!) and recompile the LCL.

With these modification the folder C:\Windows\WinSxS opens instantly.

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Quote
"Since in your solution HasChildren is valid only after painting ... "

Uh, really, i better had mentioned that, in my approach i initialized "HasChildren" anyway at node's creation time by an explicit assignment, before revisiting the property when the node first becomes visible. Would be a bad idea to let it undefined.

wp, I think i understood how your changed approach should work, and indeed it's a more proper and clean strategy, generic, world class A, pretty cool  :)

But, couldn't compile so far for to test and verify, due to other release-specific dependencies; i'm still on 2.0.12. Need to wait until i have the 2.2 installed or maybe an RC (not quite sure yet).

So i set this topic to something like "waittest 2.2" and come back to it later. Would that be ok?
Great thanks for all of your inputs and interest, and especially for this code change proposal!
At the end it could power up a folder tree app again a lot and let it work extremely fast even for (admittedly:) maybe rarely occuring use cases (highly populated folders).
Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
Need to wait until i have the 2.2 installed or maybe an RC (not quite sure yet).
No risk with 2.2RC1 (which is available now) when you select the "secondary installation" in the installer (also: specify a new directory for the config data, and do not register any file types to this version) - this way your current installation is left untouched.

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
wp, as Info: parallel install RC1 with a separate install dir and config leaded me to a problem; i'll address that in the RC1 thread and need to have it sorted out before.
Lazarus 3.2  FPC 3.2.2 Win10 64bit

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
wp, the first compile  of the project (here: the modifeied shwellview demo) with RC1 without the 3 changed files was ok. That lets me assume that the right lazarus dir (= from RC1) is used, as within the 2.0.12 folder the 3 changed files are still herein, which leaded fo the compile errors as mentioned yesterday.

But, now, if i copy the tree files on top of the RC1, the same compile error does occur.
So i'm not sure whether there are other dependcies that i'm not yet aware of?

They are starting from a missing PageClass (from customnotebook.inc), then are about mismatch of definitions within listitem.inc, and thre are some others (from customlistview.inc, comctrls.pp ,, in total 12 errors)

Sure that the installed RC1 is sufficient to let the changed files work?

PS: there's also a strange thing thing with a Dwarf-setting dialog (new for me), but i spare that out for now

Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
Sorry. There is a week between tagging of v2.2 and my changes, and since comctrls contains many components there is a chance that some other changes may have crept in.

Ideally you should use svn or, in a few days, git to get the full trunk version - then everything would fit together. Or you must extract the differences manually and copy them into the original files. You can use a diff utility (e.g. WinMerge, or TextDiff) to find the differences.

I think these are my modifications:

- At the end of TNodeState I added "nsValidHasChildren"
- I added an event type, TTVHasChildrenEvent
- I modified TTreeNode.GetHasChildren, SetHasChildren
- In TCustomTreeView, I added a private variable FOnHasChildren: TTVHasChildrenEvent, a function NodeHasChildren and a "property OnHasChildren: TTVHasChildrenEvent read FOnHasChildren write FOnHasChildren" which is published in TTreeView and TShellTreeView.

When you search for these items in my modified files you can probably see the differences and copy the new code over the original files.

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Thanks wp, no problem, i thought so.
I'll choose the latter method (integrate manually), but it will takes a bit, due to some interrupts for a day or so. Sorry for a bit delay!
« Last Edit: July 13, 2021, 08:28:57 pm by d7_2_laz »
Lazarus 3.2  FPC 3.2.2 Win10 64bit

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Integrated the changes (hopefully correct) within RC files and could compile again.
I noticed that, when opening a drive's node (in shellview demo), only the root folder is listed, and i don't know if it's by intention.

I would have expected that all root folders are listed, awaiting now i could set the property HasChildren for those of them that become visible, with the help of the newly introducted event callback "OnHasChildren"
(that one would be a function; right? As something like: function TfMain.STVOnHasChildren(Sender: TCustomTreeView; Node: TTreeNode): Boolean;)

Or would major parts or the demo to be rewritten?
Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
Call me stupid, but where is shellviewdemo? Please don't hunt me through all previous posts related to this topic.

I place a TShellTreeView on the form, compile and run, and I see all the drives on my system, all of them with a "+" so that they can be opened. I open C:\, then Windows, then WinSxS, and WinSxS opens instantly (after the modifications, of course).

When otHidden is included in ObjectTypes, then I see C:\$Recycle.Bin. What worries me is that this folder cannot be expanded (has no "+") - although in Windows Explorer it can. But this must be an old issue because the same test program compiled with Laz 2.0.12 behaves the same way. It could be that I do not have the permission to open the objects in c:\$Recycle.Bin (at least, this is what Windows Explorer tells me)

When otNonFolders is included in ObjectTypes (in addition to otHidden) c:\$Recycle.Bin can be opened. And I see individual files, and even hidden files such as pagefile.sys or hyberfil.sys.

So, my impression is that the modified TShellTreeView behaves correctly. If you don't think so, give me exact steps how to reproduce this.

When a TShellListview is attached to the ShellTreeView, however, WinSxS opens about as slow as before because now the ListView opens all the 15000 directories, so all this effort was rather useless...

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Hello wp, oops ... this demo (from GetMem) was from the thread about  ShellTreeView and system icons [page 3] , in which you hand been involved, so i assumed you remembered; sorry, my fault! I used it for some tests and cross checks here.

Testing with TShellTreeView in the IDE: yes, i'm seeing only _one_ folder when opening a drive node. Independently from the setting of otHidden or otNonFolders.
I simply had placed a shelltreeview on the forms and tried with various object types. Only one subfolder per expanded node  (cannot exclude that one of my 3 adapted files not yet fully correct).
And i think that should not be. When expanding a node, it should show all subfolders (including even hidden ones depending on an option). This is not time expensive at all!  But deciding for all of them (maybe 18000) whether they appear to be expanded, by digging on file-IO base / loop within each single one of them, that doess cost the major part of the time.

Btw, testing within the IDE: the event "OnHasChildren" does not appear here in the event list. Yoe see it in the inspector? How did you set it? 

About $Recycle.Bin: as told, had been one of the 5 deviants in my own alternative approach where i said: one might ignore that. Because maybe it's a philosophical question if Recycle.Bin show up as to have subfolders or not ...

My intuition is telling me: maybe this new approach might be a bit hard to understand and to reimplement within an app.

My own approach (last version:  simply provide a, so to say, generic, neutral, multi-purpose event slot before HasChildren is used, and then it is super-easy to use it for the eg. for the HasChildren stuff. Not more than 3 lines within treeview.inc (see above an example as "OnCustomGetSettings") and the processing time in a folder tree might shrink drastically. The cost is: conceptaally it does not look very nice or straight. But it may be harder to make the shellview demo concept-ready ...

Please ignore  the listview in this context, that's another story (it has something to do if it's virtual or not).
Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
I don't want to use this demo because there were so many experiments and tests.

Starting point must be an empty form with a TShellTreeView. Without changing anything the tree is empty in the IDE, this is different from Delphi but has been like this all the time here because PopulateWithBaseFiles exits immediately at the start when in design mode. When I run this simple project, however, I see all the drives as described in the previous mail.

Returning to the Object Inspector, when I set Root to "C:" I see only the root node at designtime, again because the tree is not populated at designtime. Therefore I do not understand when you say "Testing with TShellTreeView in the IDE: yes, i'm seeing only _one_ folder when opening a drive node." Again, when the program runs, all the subnodes of c: are added.

testing within the IDE: the event "OnHasChildren" does not appear here in the event list. Yoe see it in the inspector? How did you set it? 
Did you publish the event for the TShellTreeView in unit ShellCtrls?
But the event is only needed when you want to use your own method to detect child nodes. The event is fired by the virtual function "NodeHasChildren". TShellTreeView overrides this function such that, when no event handler is assigned, the old HasSubDir function is called.

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Yes, OnHasChildren is published.
"Root" stayed empty, for to start with the drives.
"Within the IDE" did mean, not the demo is used, but the thing is newly created in the IDE by placing the control etc. . -  Running the app within the IDE, or by clicking the executable, didn't make a difference.
Maybe we would can shorten it if you try to run my "mini-project", only containing the shelltreeview, and see, if you result in more than one folder beyond C:\. ?
If yes, it's eighter a mistake in my exchanged files (I'll add them here, maybe could a diff against yours for to assure), or something else in the envrionment.
Lazarus 3.2  FPC 3.2.2 Win10 64bit

wp

  • Hero Member
  • *****
  • Posts: 11858
I see all drives - see attached screenshot.

Your ShellCtrls is lacking the loop over the found files in TCustomShellTreeView.PopulateTreeNodeWithFiles. Here is the correct version (with the old commented stuff removed):
Code: Pascal  [Select][+][-]
  1. { Returns true if at least one item was added, false otherwise }
  2. function TCustomShellTreeView.PopulateTreeNodeWithFiles(
  3.   ANode: TTreeNode; ANodePath: string): Boolean;
  4. var
  5.   i: Integer;
  6.   Files: TStringList;
  7.   NewNode: TTreeNode;
  8.   CanAdd: Boolean;
  9. begin
  10.   Result := False;
  11.   // avoids crashes in the IDE by not populating during design
  12.   if (csDesigning in ComponentState) then Exit;
  13.  
  14.   Files := TStringList.Create;
  15.   Items.BeginUpdate;
  16.   try
  17.     Files.OwnsObjects := True;
  18.     GetFilesInDir(ANodePath, AllFilesMask, FObjectTypes, Files, FFileSortType);
  19.     Result := Files.Count > 0;
  20.  
  21.     for i := 0 to Files.Count - 1 do
  22.     begin
  23.       CanAdd := True;
  24.       with TFileItem(Files.Objects[i]) do DoAddItem(FBasePath, FileInfo, CanAdd);
  25.       if CanAdd then
  26.       begin
  27.         NewNode := Items.AddChildObject(ANode, Files[i], nil);
  28.         TShellTreeNode(NewNode).FFileInfo := TFileItem(Files.Objects[i]).FileInfo;
  29.         TShellTreeNode(NewNode).SetBasePath(TFileItem(Files.Objects[i]).FBasePath);
  30.       end;
  31.     end;
  32.   finally
  33.     Files.Free;
  34.     Items.EndUpdate;
  35.   end;
  36. end;

d7_2_laz

  • Hero Member
  • *****
  • Posts: 511
Yes, right, the missing line gots a victim of a misplaced edit action late evening  :o
- thanks!
Much better now, even heavy populated folders are opened lightning fast  :)  :)
So i hope that for you it had been a well spent time though.

The second good news is: the change is directly usable by the shellview demo mentioned too, Which benefits without further changes.
-
One little side node (only an observation): surprisingly the first open is faster than subsequent ones (folder expand -> collapse --> expand); not severe at all, but a bit curious.
-
For me it's now the task to retransport the principle of the ShellControls changes into my own app, that is working generically without the ShellControls ..
« Last Edit: July 15, 2021, 10:08:30 am by d7_2_laz »
Lazarus 3.2  FPC 3.2.2 Win10 64bit

 

TinyPortal © 2005-2018