Lazarus

Programming => LCL => Topic started by: Alexandr F on February 21, 2016, 06:14:51 pm

Title: ShellTreeView and system icons
Post by: Alexandr F on February 21, 2016, 06:14:51 pm
I would like to use ShellTreeView with system icons (like in Windows Explorer). I use Windows.

I try next:
 
IconListHandle := SHGetFileInfo('', 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
TreeView_SetImageList(ShellTreeView1.Handle, IconListHandle, TVSIL_NORMAL );

But icons are not displayed. In Delphi 7 this code work properly, icons are displayed.

I'm try to read back a handle of Imagelist form ShellTreeView:

IconListHandle := TreeView_GetImageList(ShellTreeView, TVSIL_NORMAL );

and IconListHandle always is zero (I think should be not zero).
Title: Re: ShellTreeView and system icons
Post by: PeterX on November 22, 2017, 03:40:18 pm
http://forum.lazarus.freepascal.org/index.php/topic,12300.msg62917.html#msg62917
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 07, 2021, 02:26:56 pm
Trying to port D7 apps to Lazarus (2.0.10, on and exclusively for Windows 64 bit), i get stuck with this issue TreeView_SetImageList for to retrieve System icons and to use them in TTreeView.
Yes, such similar does work for Listview and Get/Set_Imagelist. But not for Treeview/ShellTreeView.
-
Seeing all the workarounds along (here and formum DE) dealing with this issue, they are working with clones/copies of the system icons storing them in an own ImageList.  But that's not the preferred way i try to do it. I would like to assign the system image list to a treeview image list directly. Otherwise, performance decrease with many file sytem objects will occur.
Unfortunately, a direct handle assignment won't work too, so TreeView_SetImageList  would be my last resort.
-
My question is: is there any concrete sample that proves by evidence that TreeView_SetImageList  will work with the LCL treeview?
Or is it so that this will never work because the lcl treeview is constructed differently (following the cross platform paradigm)?
If the latter is true: what's the recommended strategy to follow?

Title: Re: ShellTreeView and system icons
Post by: wp on February 07, 2021, 03:50:03 pm
It would be interesting to see if this worked before Laz 2.0. If it did it would be an indication that the issue was created by the redesign of the new multi-resolution image list for Laz 2.0+.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 07, 2021, 04:22:20 pm
Thanks for attention! No, i cannot answer this; i'm new with Lazarus as of current 2.0.10.
But reading heavily around in all forums available i found the question raised along the last ten years repeatedly, without never receiving a concrete concise positive reply. What i solely found are a couple of workarounds, dealing with copies of the system images with icons/bitmaps that need to be created and stored in a target imagelist that, then, is assigned to the treeview.images.
That's too much traffic when dealing with much objects. - So i would assume it did never work.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 10, 2021, 02:38:23 pm
Sorry for to ask again:
Did anybody work successfully with TreeView_SetImageList for to attach Windows system icons to a treeview?

Or does anybody know whether that won't work by principle (due to a special implementation of the widget)?
Does anybody know a good approach without series of physical copies?

Any hint would be appreciated very much!
Title: Re: ShellTreeView and system icons
Post by: Ally on February 10, 2021, 05:05:09 pm
Hello d7_2_laz,

the post is a bit older, but maybe it will help you:

https://forum.lazarus.freepascal.org/index.php/topic,12300.0.html
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 10, 2021, 05:54:15 pm
Hello Ally,
thanks for having replied! Yes, i already noticed this article, but at the end it's about ListView_SetImageList (which is working fine .... - underlying Windows control) and not  about TreeView_SetImageList (which for me has no effect at all.   as maybe Lazarus Treeview for the Windows platform does not use an underlying Windows control - cross-platform perspective -  ., i would guess ) .
Although the artiicle's example is commented as "You can do same thing for TShellTreeView. ".  It seems not, and exactly this is the problem.

And yes, there are many examples one may find that spend system icons to the treeview, using icon and/or bitmap copy operations. Regarding speed that's not the way it should be expected to work.
In Delphi you can attach system image list to a treeview by hanle assignment. In Laz. not, as it does not expose those handle.
Alternatively in Delphi you can call TreeView_SetImageList  tor attachment, but after a lot of testing and readiing it appears to me that won't work.

So i did raise the quesetion, whether it is expected to work or not, and, if not, what should be the recommended strategy.
Title: Re: ShellTreeView and system icons
Post by: ASerge on February 10, 2021, 07:03:57 pm
So i did raise the quesetion, whether it is expected to work or not, and, if not, what should be the recommended strategy.
In lazarus TListView is rendered entirely via a widget, but TTreeView images are rendered using LCL.
Use OwnerDraw, extracting icons from the system image list as needed.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 10, 2021, 08:45:03 pm
@d7_2_laz

See attached project.
Title: Re: ShellTreeView and system icons
Post by: wp on February 10, 2021, 11:18:38 pm
GetMem, can I ask you to complete the example with code for the TShellListView. It's an excellent sample for reference.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 11, 2021, 08:37:12 am
GetMem, can I ask you to complete the example with code for the TShellListView. It's an excellent sample for reference.

Attached a full example(TShellTreeView, TShellListView included).
Title: Re: ShellTreeView and system icons
Post by: wp on February 11, 2021, 12:19:22 pm
Thanks a lot. Very helpful. Where to put it? wiki? CCR/applications? Lazarus/examples/?

Looking through the code I found two things for improvement (see my attachment):
- Fixed scaling of the image sizes (tested on Win 96ppi and 144ppi).
- No more explicit conversion UTF16ToUTF8 needed since FPC3+ where it is handled automatically.
Title: Re: ShellTreeView and system icons
Post by: El Salvador on February 11, 2021, 12:40:09 pm
And in Linux, is it more difficult to do same thing?
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 11, 2021, 12:44:50 pm
Kind thanks to all of you that have replied!
GetMem, this is the best demo i could find around! I would fully support wp that it should have the best place as a reference for all that are fighting with this problem. At least for the Windows part - in the Windows world shell explorer controls without system icons are absolutely not thinkable!

However i have two remarks - please apologize a laz. newbie.
The first thing is that we need interims copies of the icons again - AddIcon, so using not iIcon (index only) but hIcon (access to the icon object) in this operation.
The icons are copied and afterwards they will be accessed again for the drawing.
You will see noticeable speed difference when eg. (this is my standard check) opening windows\system32 with thousand objects; compare the opening time against the ms explorer. Or compare against any Delphi app that was able to to direct icon list attachments by handle assignment, or via TreeVew_SetImageList.
Yes - in most of the normal uses cases it is finely fast and fast enough. Thinking in the perspective of a file explorer there is room for speed improvement.

For those reason, for the Listview i would prefer to use ListView_SetImageList.
The link mentioned by Ally (above) gives a good starting point. As well as that one:
https://forum.lazarus.freepascal.org/index.php/topic,35669.msg236441.html#msg236441
Here, see routine TShellListView1FileAdded  (although it is not needed and i would not do a call of SHGetFileInfoW for each file added; better do it only once for a folder).

The second thing is: avoid duplication of the copies. The AddIcon will (at least this is my understanding) do 6000 AddIcons of the object and Draws afterwards, and if the folder / node is entered again later, those 6000 objects will be added 6000 times again, Am i right?
At a third visit of the folder, again 6000 adds?  Please correct me if i'm in error, i would be glad.
For this reason some of the workarounds i did mention erlier try to accomplish that with a kind of duplicate check; attempts that are working more or less sufficiently.

For those two reasons i was happy about the suggestion of ASerge (use ownerdraw). I was suprised and think it makes a good sense. We could access the icon objects directly, and access them via without unwanted intermediate copy operations, and they will not inflate an Imagelist.
Personally for me and in my special context i'll try if i could achieve results here.

ASerge, did you mean TListView  is entirely built by LCL?  if it is able to allow Listview_SetImagelist, the more it would be desirable that the treeview allows such similar.

Please correct me if my thinking is wrong. And many thanks again a lot for all of your contributions!

Title: Re: ShellTreeView and system icons
Post by: wp on February 11, 2021, 01:24:41 pm
The link mentioned by Ally (above) gives a good starting point. As well as that one:
https://forum.lazarus.freepascal.org/index.php/topic,35669.msg236441.html#msg236441
Here, see routine TShellListView1FileAdded  (although it is not needed and i would not do a call of SHGetFileInfoW for each file added; better do it only once for a folder).
Why don't you use Getmem's demo and merge it with ASerge's code? Did you check that?

I think before talking about the images we should fix other speed-limiting issues. I follow your example. open the Windows folder in the tree and click on the System32 folder in the tree to open System32 in the ListView. System32 has more than 4000 files in my system, population takes about 20 seconds (the Windows explorer opens it immediately). But after clicking I see the scrollbar running up, and this is an indication of a massive speed killer: redrawing of the listview after each addition! The standard way to avoid this is to surround the population code by an Items.BeginUpdate and Items.EndUpdate pair. So, I open shellctrls, and there's a PopulateWithRoot method. Putting an "Items.BeginUpdate" outside the "try" block and an "Items.EndUpdate" into the "finally" part should prevent that massive screen repainting. And in fact the population time goes down to about 4-5 seconds.

With the Treeview, there is a similar population routine, PopulateTreeWithFiles where the Begin/EndUpdate pair is missing as well. The same with PopulateWithBase. I wrote a quick-and-dirty program which generates 5000 folders. Opening the parent folder in the tree took 5 seconds in the original ShellViews unit, but was immediate after adding the Begin/EndUpdate.

Please make these modifications on your system and verify my observations. Then I can add them to Lazarus easily.

The speed difference between TreeView and ListView indicates that there may be something else to slow down the listview. I unhooked the OnFileAdded handler which creates the images - now the System32 folder populates immediately. I exited after the SHgetFileInfoW call -- 5 seconds again. So the following code for copying the image to the imagelist is negligible!

Title: Re: ShellTreeView and system icons
Post by: ASerge on February 11, 2021, 06:03:05 pm
ASerge, did you mean TListView  is entirely built by LCL?  if it is able to allow Listview_SetImagelist, the more it would be desirable that the treeview allows such similar.
Actually, I said that only TreeView renders the icons itself, but ListView uses the system. Therefore, it is useless for TreeView to set a non-LCL ImageList. But for ListView, it works.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 11, 2021, 08:55:27 pm
@wp
Quote
Thanks a lot. Very helpful. Where to put it? wiki? CCR/applications? Lazarus/examples/?
You're welcome. I don't have any preference, it's up to you.

@d7_2_laz
I attach a very fast solution(similar to windows explorer). After you build the application, make sure you run it outside the IDE, because the scrolling can be slow in debug mode. It looks like a TShellListView, but much faster. A TShellTreeView can also be implemented in the future.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 11, 2021, 11:02:18 pm
ASerge: thanks for clarification, that had been my original assumption, but now i am clear that's useless to think any longer about treeview und set_imagelist.

wp:  you did ask about the merge. I'm late today and hopefully my  _attachment_  is not so far behind your recent thoughts.  It's the code sample from getmen with your adaption, and for the listview the adaption which uses listview_setimagelist. Note that a separate imagelist must not be attached to the listview at the same time, Regarding the treeview nothing had been changed. ASerge gave the idea (use ownerdraw for the icons)  but i had not time yet to implement that.
About the performance, i need to clarify that i'm not using the shell controls but, file system oriented, Searchrec, custom treeview and for the listview, ownerdatafetch (OnData, virtual approach). So myself i have no performance problems und simply the need to fetch system icons for the treeview correctly according to the Lazarus concepts, and hopefully not slower than before.
Regarding the shell controls:  we did speak about them because that icon stuff mostly is discussed around thiese controls, but using treeview or shell treeview doesn't make any difference in this respect.

About the BeginUpdate / EndUpdate (resp. LockWindowUpdate  & co) - yes absolutely, if this is not implemented in the shell controls yet, it should urgently, because this does a big deal. Actually i need to focus onto the most hindering issues forrced by component incompatibilities; next will be PageControl (replacement needed) and watch threads (argh).

getmem: many thanks for the sample! Will this trunk needed be part of the next release?  I think i can see the idea behind it i(it is based on VirtualTreeview control, and so i believe by my own usage of this control  that it will be really fast enough).
Abut the icon access within: same question i had but cannot answer fo rmyself: will the continuous usage of Imagelist IconAdd, when iterating maybe the same directory multiple times, let the imagelist grow and grow and grow??
   Data := VDT.GetNodeData(PaintInfo.Node);
   .....
    FileIcon.Handle := FileInfo.hIcon;
    Data^.FImageIndex := ilShell.AddIcon(FileIcon);
    Data^.FImageIndex := ImageList_ReplaceIcon(ilShell.ResolutionByIndex[0].Reference.Handle, Data^.FImageIndex, FileIcon.Handle);
    ImageList_Draw(ilShell.ResolutionByIndex[0].Reference.Handle, Data^.FImageIndex, PaintInfo.Canvas.Handle, R.Left + 2, R.Top + (R.Height - FileIcon.Height) div 2, ILD_TRANSPARENT);

Btw: you have a good forum here! As a newbie i'm impressed about the level of discussion and contributions.




Title: Re: ShellTreeView and system icons
Post by: wp on February 12, 2021, 12:49:41 am
GetMem's solution with VirtualTreeView is amazingly fast. Why?

People usally say: "VirtualTreeView is much faster than TTreeView and TListView". But I think 4000 nodes is not so much that this would be visible.

Let's have a closer look at the OnFileAdded method of the D7_2Laz's version with TShellListView:
Code: Pascal  [Select][+][-]
  1. procedure TfMain.SLVFileAdded(Sender: TObject; Item: TListItem);
  2. var
  3.   FullName: WideString;
  4.   FileInfo: TSHFileInfoW;
  5.   SysImageHandle: DWORD_PTR;
  6.   ListHandle: HWND;
  7. begin
  8.   FullName := WideString(SLV.Root + Item.Caption);
  9.   // <----- Test #1: add "exit" here
  10.   SysImageHandle := SHGetFileInfoW(PWideChar(FullName), 0, FileInfo, SizeOf(FileInfo), SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
  11.   // <---- Test #2: add "exit" here
  12.   if SysImageHandle = 0 then
  13.      Exit;
  14.   ListHandle := SLV.Handle;
  15.   if ListView_GetImageList(ListHandle, LVSIL_SMALL) = 0 then
  16.   begin
  17.     SetWindowLong(ListHandle, GWL_STYLE,
  18.                   GetWindowLong(ListHandle, GWL_STYLE) or LVS_SHAREIMAGELISTS);
  19.     ListView_SetImageList(ListHandle, SysImageHandle, LVSIL_SMALL);
  20.   end;
  21.   Item.ImageIndex := FileInfo.iIcon;
  22. end;
  23.  
Populating c:\Windows\System32 takes about 5 seconds on my system. Let's find the speed-limiting step in this code. In a first test add an "exit" before SHGetFileInfoW -- now the listview is populated immediately. In a second step add an "exit" AFTER SHGetFileInfoW -- population takes 5 seconds again.

This observation confused me a lot because GetMem's VTV solution has exactly the same function calls. So why is it so fast?

The essential difference is: the ShellListView code has these calls in the OnFileAdded event; this is called for every file stored in the listview. VTV, however, has these calls in the OnDrawNode event handler, and this is called only for the visible files -- some tens compared to some thousands. That's the reason why the VTV Solution is so fast.

And I guess that if the TShellListView had an OnCustomDraw event which would draw only those icons needed it would be much faster. But hey, TShellListView inherits from TCustomListview, and this one does have several protected OnCustomDraw* events - so TShellListView can use them too once they are published or made accessible by a typecast to a class derived from TShellListView.

It's too late for me to demonstrate this today, but maybe tomorrow.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 12, 2021, 07:26:38 am
@d7_2_laz
Quote from: pascal
many thanks for the sample!
You're welcome!

Quote from: pascal
Will this trunk needed be part of the next release?
The next release(2.0.12) is a bugfix release. After that, what is now trunk will become Lazarus 2.2. Anyways in my previous post I attached two demo versions, one for 2.0.10 and one for trunk. Unfortunately the VirtualTreeView shipped with 2.0.10, it's slightly old and buggy. The fix is easy, just install "VirtualTreeView V5" with OPM(Menu->Package->Online Package Manager).
So both attached demo versions should be usable.

Quote from: pascal
I think i can see the idea behind it i(it is based on VirtualTreeview control, and so i believe by my own usage of this control  that it will be really fast enough).
Yes. I believe it will be fast enough, but please run a few test.

Quote from: pascal
Abut the icon access within: same question i had but cannot answer fo rmyself: will the continuous usage of Imagelist IconAdd, when iterating maybe the same directory multiple times, let the imagelist grow and grow and grow??
Each icon is loaded only once. When you do a lot of drawing(scrolling the tree for example), the icons are re-used. The imagelist won't grow, it's cleared before the tree is populated.



@wp
Your assessment is spot on, the bottleneck is SHGetFileInfoW api. The fact that VirtualTreeView is only drawing the currently visible items helps a lot. I intentionally choose VirtualDrawTree(not VirtualStringTree) because you can fine tunning the drawing process. According to the documentation SHGetFileInfoW can be speed up by passing the SHGFI_ATTR_SPECIFIED flag, but then the folder icons must be retrieved from somewhere else. Unfortunately I cannot allocate more time to implement this idea.


PS: I just realised that both in my demos I made a typo, instead of SHGetFileInfo I should have written SHGetFileInfoW.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 12, 2021, 11:36:41 am
@GetMen: yes - your ShellView_VDT example is massively impressive ... i would have expected just soemthing like that when using the VirtualTreeView control
@GetMen and @wp:
Drawing only the parts of the visible items is the best one can do, and so - for a "normal" ListView - using ownderdatafetch aka OnData is a Must for expensive operations.
If i only could rewrite most of my app from the scratch (too many dependencies), for the TreeView i would bet on the Virtual* component now. As i don't want to do that, i need to refocus the treeview.
But for anybody who is interested: are you aware of the VirtualShellTools sample explorer app (they wrote it once based on the VST). Might be worthy to inspect or study anyway!

Spot back to the "normal" Treeview":
- i'm suffering not so much with the time spent for SHGetfileInfo[W]. My D7 app is using that too and was fast enought.
- Purely trivial i'll need the system icons, sigh ...
- i'm still not sure about the usage of AddIcon (triggered by OnGetImageIndex or OnData or whereever.
Quote
Each icon is loaded only once. When you do a lot of drawing(scrolling the tree for example), the icons are re-used. The imagelist won't grow, it's cleared before the tree is populated.
Could you please explain a little bit where (for to avoid to store the same object again and again) this clear is done when coming back to a previously visited folder again?
Please apologize my question, maybe this is trivial. The answer is important for my further steps.
- Just trying the possibilities of the customdraw (icons) approach. I got stuck on one certain issue (solely treeview lcl-related) and come back to this with a question later this day.






Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 12, 2021, 12:45:51 pm
Ownerdraw treeview icons
Using AdvancedCustomDrawItem, as i already use that and appears to be the right place
Looking onto "regular" nodes (that are not cdsSelected or cdsFocused in State:
1. Simpe question (for to go sure)
Unless i set DefaultDraw to False (which i try to avoid because otherwise i would need to repaint the whole tree eleemts including vertical lines, expand buttons etc.)
the node caption / texts will be default drawed. I have no chance to avoid that. I only can / have to erase the already default drawed caption texts
eg. via FillRect in stage cdPostPaint and to DrawText the caption on an other position later.
Is this assumption right?
2. Problem :-(  :
If we draw the image and shift the subsequent Drawtext on another position:
   txtRect :=  Node.DisplayRect(True);
   inc(txtRect).Left, iconOffset);
   DrawText( ....., txtRect, .... ):

then there will be a conflict between the painted text position and the internal bookkeeping of the node offset eg. for mouse click, hitPosition et.al.
For mouse click, the control will think the caption is on the "old" offsets, and if we click for instance on the righter part of the caption, nothing will happen.
---> Painted offset und internal offset will be no longer in synch.
Any chanse to avoid? Can we tell the node by a single property that selection coordinates will start at pos X, but at pos X plus 20?
Or do we nned to apply a dummy imagekust with a dummy icon, that is default drawed and repainted afterwards, for to preserve the right selection offsets?
Title: Re: ShellTreeView and system icons
Post by: wp on February 12, 2021, 01:00:47 pm
You seem to make more  progress than me, I am still fighting how to understand that damned over-complicated customdrawing of the ListView. Can you post the code that you have so far?
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 12, 2021, 01:19:11 pm
wp, yes of course, but it is still a construction zone and the icon is still a pure placeholder
Currently i'm stuck with mouse click will not respect the changed offsets.
The procedure (only this procedure) is attached.
Title: Re: ShellTreeView and system icons
Post by: wp on February 12, 2021, 01:28:32 pm
Quote
Each icon is loaded only once. When you do a lot of drawing(scrolling the tree for example), the icons are re-used. The imagelist won't grow, it's cleared before the tree is populated.
Could you please explain a little bit where (for to avoid to store the same object again and again) this clear is done when coming back to a previously visited folder again?
I think d7_2_laz's argument is valid. I took the demo of reply #12 (which does add icons to the image list) and added the line Caption := SLV.SmallImages.Count.ToString; at the end of the SLVFileAdded event handler -- it simply displays in the form  caption the current count of icons of the ListView's imagelist. When I run the program and open the c:\Windows\System32 folder the icon count increases by about 100 on my system. Then I click some other folder, and the count increases again by some value. But when I return to C:\Windows\System32 the icon count increases by aonther 100 icons. This is a clear proof that the icons are re-added, otherwise the icon count should stay constant when a previously opened folder is revisited.
Title: Re: ShellTreeView and system icons
Post by: wp on February 12, 2021, 01:40:46 pm
wp, yes of course, but it is still a construction zone and the icon is still a pure placeholder
Currently i'm stuck with mouse click will not respect the changed offsets.
The procedure (only this procedure) is attached.
Sorry, you did say that you work on the treeview but I did not get it... I am struggling with the ListView, and this is even worse.

[EDIT]
Found the reason why it is so difficult for me to owner-draw the listview: I am working with Laz trunk, and the fix in 64204 (https://bugs.freepascal.org/view.php?id=25397) seems to have broken owner-drawing.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 12, 2021, 02:44:18 pm
@d7_2_laz
Quote
Could you please explain a little bit where (for to avoid to store the same object again and again) this clear is done when coming back to a previously visited folder again?

@wp
Quote
I think d7_2_laz's argument is valid. I took the demo of reply #12 (which does add icons to the image list) and added the line Caption := SLV.SmallImages.Count.ToString; at the end of the SLVFileAdded event handler -- it simply displays in the form  caption the current count of icons of the ListView's imagelist. When I run the program and open the c:\Windows\System32 folder the icon count increases by about 100 on my system. Then I click some other folder, and the count increases again by some value. But when I return to C:\Windows\System32 the icon count increases by aonther 100 icons. This is a clear proof that the icons are re-added, otherwise the icon count should stay constant when a previously opened folder is revisited.

The ImageList is only cleared in my latest example, the one with VirtualDrawTree(see method PopulateTree). This must be done in the previous examples too, otherwise the size of the ImageList will increase. If you choose to go with custom draw, imagelists might not be needed at all.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 12, 2021, 03:48:29 pm
Sorry for the mistake wp, i add the sample for the listview ownerdraw here (two derivats), but i think it is not very meaningful iin our special context and is not scoping icons (which are still retrieved va listview_setimagelist).

Thanks GetMem for the reply, i take a look inside the example you mentioned this afternoon.
Title: Re: ShellTreeView and system icons
Post by: wp on February 12, 2021, 05:12:16 pm
Now the modified ShellViews demo is working in which ListView icons are no stored in an imagelist any more, but are painted on the fly at runtime. Opening c:\windows\system32 with about 4000 files on my system takes now about 1 second (strangely enough the folder opens instantly on my VM with Win7, other than my Win10 "real" machine).

You will not see this on an original Laz 2.0.10. You must add BeginUpdate/EndUpdate pairs are used whenever the Listview is populated (method SetRoot (around Clear, Items.Clear, PopulateWithRoot), method PopulateWithRoot (range like "Files")). The recent revision of trunk contains these changes already, it also contains the fix of the owner-draw regression.

There are still some minor painting issues (no painting of subitems, issues with painting the selected item).

Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 12, 2021, 07:18:20 pm
You both are just doing milestones on improvements of the shell controls, are you aware?  :D

@wp: the icon thing within  the SLVCustomDrawSubItem basically works fine  :D
I still need to adapt the ShellCtrls.pas as mentioned, but if the BeginUpdate/EndUpdtes is not implemented here yet, i don't doubt about the effect.

First back to the treeview (my first pain), topic "AddIcon" only for the moment:
meanwhile i thought about, what would be effect if we query on the initial value (-1) before doing the AddIcon.
@GetMen: Then i tried again the VDT sample and verified (display of listimage count in the caption) that indeed it behaved correctly.  After some srolling up and down (all getData done) the counter did reach the maxium number of objects within the folder ...... but no longer increased above..
I looked inside the source and saw that you did exactly that (query on -1 as condition for the AddIcon steps).  The clue is not the clear of the listview. It is this if-condition.

@wp, would you like to verify within your last sample? 
At the beginning of STVGetImageIndex add:
Code: Pascal  [Select][+][-]
  1.   fMain.Caption := ilSTV.Count.ToString;
  2.   if Node.ImageIndex > -1 then exit;     // Probably: -1 seems to be the initial value of the node (no need to set i explicitely?
  3.  

Try it with and without this if-condition and iterate along folders. Look at the results.

So we have for now at least a working version of the treeview with icons, and without havng 5 million entries in the imagelist after an hour  8-)



Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 13, 2021, 04:29:01 pm
With Delphi we can do something like this:
Code: Pascal  [Select][+][-]
  1. ImageList.Handle := SHGetFileInfoW('', 0, FileInfo, SizeOf(FileInfo), SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
  2. ImageList.ShareIcons := True;

After the assigment, ImageList has access to system icons. Unfortunately the Handle property is readonly in Lazarus, so we must resort to hacks like ImageList.Add.
To completely remove the need of ImageList, I wrote a unit which gets the icons faster then the previously used methods and supports different icon size(ShellView_GetIcons.zip) .
I also attach the VirtualDrawTree demo, where TImageList is not used anymore. A directory with 10000 items can be loaded in less then 100 ms(ShellView_VDT_Trunk.zip). The same idea also works with custom drawn TShellListView, though  VDT is much faster.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 13, 2021, 10:01:07 pm
Finally the super fast TShellTreeView/TShellListView combination has arrived. :D  Please run the application outside the IDE. Unfortunately I had to switch back to ImageLists, because owner drawing is not working in TShellListView or at least not working properly.

PS: I deatach myself from this thread, enough shell programming for a year. :)
Title: Re: ShellTreeView and system icons
Post by: wp on February 13, 2021, 11:30:29 pm
Now I uploaded a new version of TShellTreeView to Lazarus trunk in which the shell icons are automatically displayed when no imagelist is attached to the tree. This means no more user code is required to show the icons. Of course, it works only on Windows...

The feature can be turned off by setting the public property UseBultinIcons to false. Or when an ImageList with special icons is attached to the tree then these images have priority.

Unfortunately customdrawing in T(Shell)ListView is much more complicated, but I try to get a similar solution also for the ShellListview.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on February 14, 2021, 10:54:47 am
@you_both,

i cannot unerline anough how impressed i am about your contributios.
Are your aware that there had been threads around this topic since long time ago, eg. since 2009(!) in a long thread in the german forum?
Your contribution changes all. Modern speaking: 10000 likes from my side!

I meanwhile had integrated the OnGetImageIndex paradigm (for treeview) in my own context and the listview_setimage list approach in my virtual listview
and was very happy to have the icons for the treeview and in general the speed back i was used to have with the previous D7 handle assignment.
The new versiom for the listview is even faster and i try to adapt accordingly. Great!

About the listview ownerdraw a little remark: occasionally i noticed that with this version leaving a heavy populated folder took nearly the same time as entering it:
I wondered where the time was eaten, maybe in intensive internal graphical cleanup things, don't know. Might be considered for such an approach.
However the new version does not shou up this, it is simply excellent and it is great to hear that such will be part of the shell controls later.
 8-) :) ;D
Title: Re: ShellTreeView and system icons
Post by: jcmontherock on February 15, 2021, 11:10:41 am
@you both,

I tried to reuse your example, GetMem in your post #31 (unit usystemicons) with TTreeView. For that I changed FImageList and function GetIconIndex to public. How should I define ImageList in TTreeView ? I tried
"TreeView1.Images := TImageList(@SysIcons.FImageList)" or
"TreeView1.Images := TImageList(SysIcons.FImageList^)" but nothing works. Have you any idea ?
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on February 15, 2021, 11:31:39 am
@jcmontherock
Quote
How should I define ImageList in TTreeView ?
You can't assign FImageList to a TImageList, unfortunately the Handle property is readonly in lazarus:
Code: Pascal  [Select][+][-]
  1. TreeView1.Images.Handle := SystemIcons.FImageList; //won't work
More over AFAIK TTreeView does not support virutal mode, so your code will be slow. The best solution is to switch to VirtualDrawTree.

Title: Re: ShellTreeView and system icons
Post by: jcmontherock on February 16, 2021, 05:47:45 pm
Thanks for your infos et your examples.
Title: Re: ShellTreeView and system icons
Post by: wp on March 09, 2021, 07:41:40 pm
In current Lazarus trunk there is now a version of TShellTreeView and TShellListView in which the system icons are displayed automatically when no image list is attached. It does not utilize the last improvments which GetMem has brought up, but should be good for a start - see screenshot.

Note that this works only on Windows, for other widgetsets still no icons are displayed.

Note also that the code now has been moved into the widgetset. In the non-Windows widgetsets, "dummy" code makes sure that the widgetset architecture works. There is a potential risk that there are typos in the registration code of other widgetsets which could crash the application and/or the IDE when TShellTreeView and/or TShellListView are used. I tested gtk2, gtk3, qt4, qt5, and cocoa, but I could not test the other widgetsets supported by the LCL. Therefore I ask users of Laz trunk and these other widgetsets to test the ShellTreeView and ShellListView in a simple application, such as the attached one.
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on March 09, 2021, 08:05:48 pm
@wp

Quote
Note that this works only on Windows, for other widgetsets still no icons are displayed.
Double Commander does a pretty good job getting the icons on Linux and macOS. It's not perfect, but so far is the best from what I saw. I attach two screenshots with DC and the native file managers side by side. Unfortunately DC is a very large application, hopefully somebody has patience to extract the relevant part.

Donwload link: https://sourceforge.net/projects/doublecmd/
License: GNU Library or Lesser General Public License version 2.0 (LGPLv2)
              GNU General Public License version 2.0 (GPLv2)
Title: Re: ShellTreeView and system icons
Post by: El Salvador on March 10, 2021, 11:04:11 am
@wp

Quote
Note that this works only on Windows, for other widgetsets still no icons are displayed.
Double Commander does a pretty good job getting the icons on Linux and macOS. It's not perfect, but so far is the best from what I saw. I attach two screenshots with DC and the native file managers side by side. Unfortunately DC is a very large application, hopefully somebody has patience to extract the relevant part.

Donwload link: https://sourceforge.net/projects/doublecmd/
License: GNU Library or Lesser General Public License version 2.0 (LGPLv2)
              GNU General Public License version 2.0 (GPLv2)
I believe the code we are interested in may be in this unit: https://github.com/double-commander/doublecmd/blob/master/src/platform/upixmapmanager.pas
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on March 10, 2021, 11:37:19 am
@El Salvador
Quote
I believe the code we are interested in may be in this unit: https://github.com/double-commander/doublecmd/blob/master/src/platform/upixmapmanager.pas
Thanks! I really appreciate your effort. The final decision belongs to @wp.
Title: Re: ShellTreeView and system icons
Post by: wp on March 10, 2021, 02:41:19 pm
Thanks. Another hint is given by Anton Kavalenka in an attachment to bugreport https://bugs.freepascal.org/view.php?id=18247.

So, if somebody wants to spend some time on this topic, I'd be glad to accept related patches. Ideally such a patch should take advantage of the current widgetset solution. In case of gtk2, for example, it must provide a unit lcl/interfaces/gtk2/gtk2wsshellctrls with a class TGtk2WSCustomShellTreeView implementing the class functions DrawBuiltInIcon and GetBuiltinIconSize, as well as a class TGtk2WSCustomShellListView implementing a class function GetBuiltInImageIndex. And ideally the code should use the gtk2 functions available rather than relying on a given folder structure to find the images. And of course the same must be repeated for qt, qt5, gtk3, gtk, cocoa, carbon...

So, knowing how somebody else solved the issue is just one (important) step on some long trail.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 15, 2021, 03:01:26 pm
Hello wp,
small q. - is there any timeline when your changes / winx64 might be delivered officially?
(i'm not using the shellctrls  yet / not yet for the recent app, but would be very interested to try them out for other projects, and appreciate your efforts very much).
Title: Re: ShellTreeView and system icons
Post by: wp on March 15, 2021, 03:25:46 pm
Since it is a new feature it will be contained in Laz 2.2. Releasing 2.2 has recently been discussed in the devs mailing list, but no specific date has been mentioned.
Title: Re: ShellTreeView and system icons
Post by: El Salvador on March 15, 2021, 07:41:02 pm
Thanks. Another hint is given by Anton Kavalenka in an attachment to bugreport https://bugs.freepascal.org/view.php?id=18247.

So, if somebody wants to spend some time on this topic, I'd be glad to accept related patches. Ideally such a patch should take advantage of the current widgetset solution. In case of gtk2, for example, it must provide a unit lcl/interfaces/gtk2/gtk2wsshellctrls with a class TGtk2WSCustomShellTreeView implementing the class functions DrawBuiltInIcon and GetBuiltinIconSize, as well as a class TGtk2WSCustomShellListView implementing a class function GetBuiltInImageIndex. And ideally the code should use the gtk2 functions available rather than relying on a given folder structure to find the images. And of course the same must be repeated for qt, qt5, gtk3, gtk, cocoa, carbon...

So, knowing how somebody else solved the issue is just one (important) step on some long trail.
Maybe I could look at it myself, but I don't guarantee anything (and in a short time).

Another small thing for win32: you can improve icon recovery times by adding the SHGFI_USEFILEATTRIBUTES flag when using the method SHGetFileInfoW(). See https://web.archive.org/web/20101125055232/https://www.codeguru.com/cpp/com-tech/shell/article.php/c4511 and https://devblogs.microsoft.com/oldnewthing/20040601-00/?p=39073
Title: Re: ShellTreeView and system icons
Post by: wp on March 15, 2021, 10:56:42 pm
Thanks for the hint, I modified the win32 widgetset code to take advantage of the SHGFI_USEFILEATTRIBUTES flag. I do see a factor 2 speed improvement when the c:\windows\system32 folder is opened in the ShellListView (from about 2 sec down to about 1 sec)
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 16, 2021, 12:35:11 am
But please keep in mind that imo for win64 it does not make much sense,
because it might have inaccurate results (see image 1, left side "old"/correct behaviour, right side when using the changed attribute and flag).  The evaluation by registry type might be to rough sometimes.

And imo it's not faster here, (sometimes even slower, varies depending on the os cache). On x64 the existing settiing is excellent fast, in my test (intel i5) it needs ca. 100ms for 18000 objects. -See image 2 left/"old" vs. right/"new".
Title: Re: ShellTreeView and system icons
Post by: wp on March 16, 2021, 10:37:25 am
I can confirm the issue with the missing link overlay icons. Speed is difficult to reproduce for the treeview because it varies strongly between runs. I do not expect much impact of the new version on ShellTreeView because it needs the icons only for painting, unlike the TShellListview for which I confirmed the earlier observation of a factor 2 speed improvement.

Because of the overlay icon issues I reverted the change for the ShellTreeView, but kept it for the ShellListView.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 16, 2021, 02:03:26 pm
You see the overlay icons in the listview / Windows 64bit
having replaced the flag SHGFI_TYPENAME by SHGFI_USEFILEATTRIBUTES as the article explains to be the driver to gain the speed benefit?
If i use SHGFI_TYPENAME i'll see the overlays, but it is described as to be slower.
If i use SHGFI_USEFILEATTRIBUTES (which is described to be faster), i'll see them not.

    ImageHandle := SHGetFileInfoW(PWideChar(FileName),
                   //FILE_ATTRIBUTE_NORMAL,      // for non-folder items
                   FILE_ATTRIBUTE_DIRECTORY,     // for folders only
                  {%H-}FileInfo, SizeOf(FileInfo),
                   //SHGFI_USEFILEATTRIBUTES or   // ** is described to be faster --- but overlay icons are lost!
                  //SHGFI_TYPENAME or   // ** is described to be slower --- but the overlay icons are preserved!
                  SHGFI_USEFILEATTRIBUTES or
                  SHGFI_ICON or SHGFI_SMALLICON or SHGFI_SYSICONINDEX);

In the listview in virtual mode imo one shouldn't recognize much speed difference though as the SHGetFileInfoW should be executed only for the few lines one sees on the screen at a time, not for the 18000 items the folder might contain.

But maybe i did misunderstand something basically. But what?
Title: Re: ShellTreeView and system icons
Post by: wp on March 16, 2021, 02:38:01 pm
You see the overlay icons in the listview / Windows 64bit
having replaced the flag SHGFI_TYPENAME by SHGFI_USEFILEATTRIBUTES as the article explains to be the driver to gain the speed benefit?
If i use SHGFI_TYPENAME i'll see the overlays, but it is described as to be slower.
If i use SHGFI_USEFILEATTRIBUTES (which is described to be faster), i'll see them not.
The same for me. The current implementation of the ShellListView in Laz trunk does NOT operate in virtual mode - I think this would be a fundamental change, and doing this would probably affect the base component, not just the win32 widgetset, and I am not sure that virtual mode works correctly in all widget sets. Such a fundamental change should not be done before the new release v2.2 is tagged. Therefore I decided to focus on the speed argument and accept the incorrectly drawn overlay icons. Maybe in a later version we can try to implement virtual mode for the ShellListView, and since speed will not be an issue then we can remove the SHGFI_USEFILEATTRIBUTES again.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 16, 2021, 03:17:53 pm
Ok wp ... understood.  Thanks for the information!
Title: Re: ShellTreeView and system icons
Post by: jcmontherock on March 16, 2021, 04:48:55 pm
Strange: I do not have that problem with *.lnk files and SHGFI_USEFILEATTRIBUTES
Title: Re: ShellTreeView and system icons
Post by: wp on March 16, 2021, 04:57:10 pm
What are you doing? At which time did you pull your Laz-trunk, what is the revision that you work with? The version with SHGFI_USEFILEATTRIBUTES in TShellTreeView was online only for about 12 hours.
Title: Re: ShellTreeView and system icons
Post by: jcmontherock on March 16, 2021, 05:50:27 pm
I do not use ShellTreeView. Only a simple StringGrid to get icons.
I am on Windows 10 (19042.867) 64 bits. I am using the Lazarus 2.012, SVN 64642 downloaded from the official site.
The function I'm using is:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.GetFileIcon(FilePath: String; var fIcon: TIcon);
  2. var
  3.   FileName:     WideString;
  4.   FileInfo:     TSHFileInfoW;
  5.   ImageHandle:  DWORD;
  6. begin
  7.   FileName := UTF8ToUTF16(FilePath);
  8.   ImageHandle := SHGetFileInfoW(PWideChar(FileName), 0, {%H-}FileInfo, SizeOf(FileInfo),
  9.                  SHGFI_USEFILEATTRIBUTES Or SHGFI_ICON or SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
  10.   if ImageHandle = 0 then Exit;
  11.   fIcon.Handle := FileInfo.hIcon;
  12.  
  13. end;
  14.  
  15.  

And it works fine.
Title: Re: ShellTreeView and system icons
Post by: wp on March 16, 2021, 06:52:35 pm
I do not use ShellTreeView. Only a simple StringGrid to get icons.
I am on Windows 10 (19042.867) 64 bits. I am using the Lazarus 2.012, SVN 64642 downloaded from the official site.
The function I'm using is:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.GetFileIcon(FilePath: String; var fIcon: TIcon);
  2. var
  3.   FileName:     WideString;
  4.   FileInfo:     TSHFileInfoW;
  5.   ImageHandle:  DWORD;
  6. begin
  7.   FileName := UTF8ToUTF16(FilePath);
  8.   ImageHandle := SHGetFileInfoW(PWideChar(FileName), 0, {%H-}FileInfo, SizeOf(FileInfo),
  9.                  SHGFI_USEFILEATTRIBUTES Or SHGFI_ICON or SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
  10.   if ImageHandle = 0 then Exit;
  11.   fIcon.Handle := FileInfo.hIcon;
  12.  
  13. end;
  14.  
  15.  

And it works fine.

Really? When the flag SHGFI_USEFILEATTRIBUTES is used then the second parameter must contain the attributes of the file for which the icon is to be determined. With your value 0, I never get the folder icons.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 16, 2021, 07:29:51 pm
Yes. but however he sees the overlay icons for files ,, (0  matches FILE_ATTRIBUTE_NORMAL) and, so, the icon information for the .LNK file in speech.

I think the difference is by principle, as apparently the windows registry stores icon information for files and folders differently.  More individually resp. respecting file extension too ... like type .LNK, vs. more generic/general for the type "folder".
"c:\ Documents and Settings"  is no .LNK file.
jcmontherock, if you try that one and use attribute FILE_ATTRIBUTE_DIRECTORY instead of "0", i would guess you see no overlay icon either.
 
Title: Re: ShellTreeView and system icons
Post by: jcmontherock on March 17, 2021, 05:15:23 pm
With C:\Document And Setting I get the following:
Title: Re: ShellTreeView and system icons
Post by: jcmontherock on March 17, 2021, 05:30:31 pm
I give you the program. You only have to change the files.
Title: Re: ShellTreeView and system icons
Post by: d7_2_laz on March 17, 2021, 05:54:20 pm
Thar's resulting from the second parameter of the shgetfileinfo, the attribute. It surely contains still 0 (resp. FILE_ATTRIBUTE_NORMAL)  - for files -, but not FILE_ATTRIBUTE_DIRECTORY, which is needed to be used for folders.
With FILE_ATTRIBUTE_DIRECTORY you'll see the folder icon, but without the link overlay.
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 17, 2021, 12:58:47 pm
Hi everyone, these days I went back to the topic and did some research.

On Linux, the file icons are located in the themes folder (different folders), according to the FreeDesktop.org Icon Theme specification (https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html).

I found that the DoubleCommander authors wrote their own implementation (https://github.com/double-commander/doublecmd/blob/master/src/platform/uicontheme.pas) in FreePascal. But GTK2 / 3 and QT5 also have their own variant. So there are three cases:
- We write an implementation in Pascal (we reinvent the wheel)
- We take (with the permission of the DoubleCMD authors) and adapt (remove external unit) the class for insertion into Lazarus
- We use the QT (using QIcon_fromtheme(retval, 'mimetype') and get its QPixMap with defined size) and GTK2 / 3 (see https://github.com/GNOME/gtk/blob/f6f07ba238b4f734d9134c5e3a5e726ce8cb5fea/gtk/gtkicontheme.c#L91) libraries. But how do we draw icons (as QIcon/QPixMap or GtkIconPaintable)?
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 19, 2021, 08:44:10 pm
Some progress in QT5. See attached image.
Title: Re: ShellTreeView and system icons
Post by: wp on April 19, 2021, 10:23:30 pm
Can you post a patch?
Title: Re: ShellTreeView and system icons
Post by: balazsszekely on April 20, 2021, 06:38:06 am
@El Salvador

Quote
Some progress in QT5. See attached image.
Looks good!
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 20, 2021, 10:08:08 am
Can you post a patch?
I'm still working on it. For now I am adapting the GetMem demo for QT5. Then I'll move on to integrate the changes into Lazarus.
Title: Re: ShellTreeView and system icons
Post by: wp on April 20, 2021, 10:15:20 am
Thanks
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 20, 2021, 02:41:12 pm
I am integrating the first changes into Lazarus for QT but I am having a small issue regarding the ShellListView.

The problem is simple: while in Windows, we use the system ImageList (see https://github.com/graemeg/lazarus/blob/9d47cae65ecb458d45e05edf0bd2d26629c907e2/lcl/interfaces/win32/win32wsshellctrls.pp#L140-L146), in Linux this does not exist. So in Linux how do we do?
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 22, 2021, 07:53:08 pm
Good news, everyone. I just pushed my WIP about automatic shell icons in QT5 and GTK2 in my Lazarus repo in github. Tomorrow I will do the GTK3 version.

For GTK3 and QT5, the code is still in work in progress and therefore the patch is not yet ready for the following reasons:
- The procedure LoadMimeIconNames is quite slow and I want to try to improve it. [fixed]
- Some parts of the code are duplicated between QT5 and GTK2. This is my first time modifying Lazarus, so I don't know how to go about making things better (especially QT5 wants Widestring and GTK2 AnsiString).
- There is no recognition of special folders and therefore generic icons are used for them

Edit: Completed for GTK3 (TShellListview doesn't work, I believe for issues on the TListview) - https://github.com/salvadorbs/lazarus/commit/e92a9e40b52a8cfaacc0a1658f480ef8faf1e6a3
Duplicate code problem got worse (see GTK3), but I will make the patch anyway. Suggestions are welcome
Title: Re: ShellTreeView and system icons
Post by: wp on April 24, 2021, 10:39:36 pm
El Salvador, applied your patch. Built-in shell icons now are displayed in TShellTreeView and TShellListview for the win32, qt5, gtk2 and gtk3 widgetsets (the latter has issues already in the base classes). Thanks for this contribution.
Title: Re: ShellTreeView and system icons
Post by: El Salvador on April 27, 2021, 05:06:51 pm
El Salvador, applied your patch.
Thank you very much. These days I'll probably do a small patch (about executable files), but if someone were to test the code on the various distros in the meantime it would be great. Thank you everybody!
Title: Re: ShellTreeView and system icons
Post by: wp on May 03, 2021, 09:41:07 pm
Had to revert the patch because it broke usage of qt5, gtk2 and gtk3 on Windows (https://bugs.freepascal.org/view.php?id=38831).
Title: Re: ShellTreeView and system icons
Post by: El Salvador on May 03, 2021, 10:33:02 pm
No problem, wp.
TinyPortal © 2005-2018