Board index » cppbuilder » Re: Getting SYS Icons

Re: Getting SYS Icons


2006-03-09 03:50:48 AM
cppbuilder2
"poojo hackma" <poojo.com/mail>wrote in message
Quote
It was simple enough to copy the technique, but I want to extend it
to files *not* located on my PC (i.e. files over a network).
The demo code you posted is using ITEMIDLIST values to keep track of local
files only, and then passing those ITEMIDLISTs to SHGetFileInfo() to get the
appropriate shell image list index when needed. To do what you are asking,
you would first need to update the code to detect virtual items so that you
can specify filenames instead of ITEMIDLISTs. When calling SHGetFileInfo()
for a filename, specify the SHGFI_USEFILEATTRIBUTES flag so that a physical
file is not needed, and remove the SHGFI_PIDL flag. That way,
SHGetFileInfo() returns the index for the icon belonging to the specified
file extension. For example:
int __fastcall GetShellImage(const AnsiString &FileName, bool Large,
bool Open)
{
SHFILEINFO FileInfo = {0};
int Flags = SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_USEATTRIBUTES;
if( Open )
Flags |= SHGFI_OPENICON;
if( Large )
Flags |= SHGFI_LARGEICON;
else
Flags |= SHGFI_SMALLICON;
::SHGetFileInfo(FileName.c_str(), FILE_ATTRIBUTE_NORMAL, &FileInfo,
sizeof(FileInfo), Flags);
return FileInfo.iIcon;
}
Quote
I don't understand what to assign the fileInfo.iIcon parameter to
(TListView->Items->ImageIndex = fileInfo.iIcon?)
Yes, you assign the iCon value to the TListItem::ImageIndex property.
Gambit
 
 

Re:Re: Getting SYS Icons

Wow. Thanks!
I've got it working using the filenames instead of the file IDs, and now I
wonder why someone went through all the trouble of using ITEMIDLISTs! There
are a few files that will not show up with their icons, but the code is
simpler ...to me! :)
 

Re:Re: Getting SYS Icons

poojo hackma wrote:
Quote
I've got it working using the filenames instead of the file IDs,
Can you please show how you construct the filename ? Is it something
with the dir + DisplayName ?
Hans.
 

{smallsort}

Re:Re: Getting SYS Icons

"poojo hackma" <poojo.com/mail>wrote in message
Quote
I've got it working using the filenames instead of the file IDs, and
now I wonder why someone went through all the trouble of using
ITEMIDLISTs!
Because that is what the OS shell itself uses internally for everything, so
it is more efficient to use ITEMIDLISTs when dealing with the OS shell.
ITEMIDLISTs are generic, they don't care about Unicode vs. Ansi (since they
are not themselves strings), and they can represent things that filenames
alone cannot (virtual items in particular).
Gambit
 

Re:Re: Getting SYS Icons

Hello Hans,
All I used was the display name (no dir). My code has 2 TListViews: one for
files on the PC (lvPC), the other for files on the network (lvFtp).
My OnCustomDrawItem handler below is still messy, but it's working! A note
about the "<DIR>" text: This is something FTP servers typically return, so
I fill my TListItem->SubItems with this instead of leaving my Size field
blank.
//-----------------------------------------------
void __fastcall TMainForm::ListView_CustomDrawItem(TCustomListView *Sender,
TListItem* Item, TCustomDrawState State, bool &DefaultDraw) {
TListView* lv = dynamic_cast<TListView*>(Sender);
lv->Canvas->Font->Style = TFontStyles();
lv->Canvas->Font->Color = clBlack;
if (lv == lvFtp) {
int Flags = SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON;
TSHFileInfo FileInfo;
memset(&FileInfo, 0, sizeof(FileInfo));
char file[MAX_PATH + 1] = {0};
strncpy(file, Item->Caption.c_str(), MAX_PATH); // Filename only. No
directory
if (strstr(Item->SubItems->GetText(), "<DIR>")) {
SHGetFileInfo(file, FILE_ATTRIBUTE_DIRECTORY, &FileInfo,
sizeof(FileInfo), Flags);
} else {
SHGetFileInfo(file, FILE_ATTRIBUTE_NORMAL, &FileInfo,
sizeof(FileInfo), Flags);
}
Item->ImageIndex = FileInfo.iIcon;
} else if (lv == lvPC) {
int Attrs = ((PShellItem*)pcList->Items[Item->Index])->Attributes;
if (BOOL(Attrs & FILE_ATTRIBUTE_HIDDEN)) {
lv->Canvas->Font->Style = Sender->Canvas->Font->Style << fsItalic;
lv->Canvas->Font->Color = clGray;
}
if (BOOL(Attrs & FILE_ATTRIBUTE_SYSTEM)) {
lv->Canvas->Font->Style = Sender->Canvas->Font->Style << fsItalic;
lv->Canvas->Font->Color = clGradientInactiveCaption;
}
}
}
Quote
Can you please show how you construct the filename ? Is it something
with the dir + DisplayName ?

Hans.
 

Re:Re: Getting SYS Icons

"poojo hackma" <poojo.com/mail>wrote in message
Quote
My OnCustomDrawItem handler below is still messy, but it's working!
You are misusing that event. One of the things you are doing is using that
event to assign the TListItem::ItemIndex property for your FTP items. That
is the completely wrong place to do that. You should be setting it at the
time you add the item to the ListView, or alternatively in the
OnGetImageIndex event (which is a better choice for virtual ListViews). The
OnCustomDrawItem event is only to be used for drawing and nothing else.
Use this code instead:
void __fastcall TMainForm::ListView_GetImageIndex(TObject* Sender,
TListItem* Item)
{
SHFILEINFO FileInfo = {0};
int Flags = ( SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON );
int Attrs;
AnsiString temp;
char *p;
TListView* lv = static_cast<TListView*>(Sender);
if( lv == lvFtp )
{
Attrs = ( Item->Caption.Pos("<DIR>") != 0 )
? FILE_ATTRIBUTE_DIRECTORY
: FILE_ATTRIBUTE_NORMAL;
temp = Item->Caption;
p = temp.c_str();
}
else
{
PShellItem *pShellItem =
static_cast<PShellItem*>(pcList->Items[Item->Index]);
Attrs = pShellItem->Attributes;
Flags |= SHGFI_PIDL;
p = static_cast<char*>(pShellItem->FullID);
}
SHGetFileInfo(p, Attrs, &FileInfo, sizeof(FileInfo), Flags);
Item->ImageIndex = FileInfo.iIcon;
}
void __fastcall TMainForm::ListView_CustomDrawItem(TCustomListView
*Sender, TListItem* Item, TCustomDrawState State, bool &DefaultDraw)
{
TListView* lv = static_cast<TListView*>(Sender);
TFontStyles FontStyle = Sender->Canvas->Font->Style;
TColor FontColor;
int Attrs = ( lv == lvPC )
?
static_cast<PShellItem*>(pcList->Items[Item->Index])->Attributes
: 0;
if( Attrs & FILE_ATTRIBUTE_HIDDEN )
{
FontStyle << fsItalic;
FontColor = clGray;
}
else if( Attrs & FILE_ATTRIBUTE_SYSTEM )
{
FontStyle << fsItalic;
FontColor = clGradientInactiveCaption;
}
else
FontColor = Sender->Canvas->Font->Color;
lv->Canvas->Font->Style = FontStyle;
lv->Canvas->Font->Color = FontColor;
}
Quote
A note about the "<DIR>" text: This is something FTP servers
typically return
Not as typical as you think. There are literally dozens of completely
different, completely unrelated formats used for FTP directory listings.
This is because RFC 959 did not define a standard format for listings, so
most vendors choose to use thie own custom formats (a later draft proposal
added the MLSD command to the FTP protocol to address that, but the damnge
has already been done). If you look at the source code for Indy 10 sometime
(www.indyproject.org), you will see all of the various parser classes
that have to be used to handle all of these different vendor formats (and
more are added when they get discovered).
Gambit
 

Re:Re: Getting SYS Icons

poojo hackma wrote:
Quote
All I used was the display name (no dir). My code has 2 TListViews: one for
files on the PC (lvPC), the other for files on the network (lvFtp).

My OnCustomDrawItem handler below is still messy, but it's working!
You/I would be better off with separate CustomDrawItem eventhandlers.
That would make the code more readable.
Thanks for the info.
I see now that you used a project from the Examples directory.
Are you forced by your company to place the { } different from that what
borland uses with its functions ? It does make it less readable for me.
Hans.
 

Re:Re: Getting SYS Icons

Sorry about that, Hans. It is just a user preference for me. I've got a
small screen, and I try to see as much of the code at one time as possible.
There is also something about this coding style that makes me feel less
wasteful! ...like I'm being environmentally friendly or something. Do I
have issues, or what? :)
Quote
Are you forced by your company to place the { } different from that what
borland uses with its functions ? It does make it less readable for me.

Hans.
 

Re:Re: Getting SYS Icons

Hi Gambit,
I had to make a minor mod to get the code to run. The compiler didn't want
to cast from '_ITEMIDLIST*' to 'char*'. I dug around and found how the
example passed info to this routine, and used that technique:
p = PChar(pShellItem->FullID); // static_cast<char*>(pShellItem->FullID);
Also, it looks like once I add the OnGetImageIndex event handler for lvPC
(the virtual ListView), the virtual part appears to stop working, so I am
only using the event for lvFtp (which not virtual ...because of my limited
understanding of Shell Items used in the examples).
I have to confess: I'm not entirely sure how to make a TListView behave as
a virtual ListView, but it seems the OwnerData property needs to be true.
About "<DIR>": Drats! Looks like I need to manually ensure my TList has
"<DIR>" in there.
 

Re:Re: Getting SYS Icons

poojo hackma wrote:
Quote
There is also something about this coding style that makes me feel less
wasteful! ...like I'm being environmentally friendly or something. Do I
have issues, or what? :)
Less prints. Less paper used. Very good.
Hans.
 

Re:Re: Getting SYS Icons

"poojo hackma" <poojo.com/mail>wrote in message
Quote
Also, it looks like once I add the OnGetImageIndex event handler
for lvPC (the virtual ListView), the virtual part appears to stop working
Please elaborate.
Quote
so I am only using the event for lvFtp (which not virtual ...because
of my limited understanding of Shell Items used in the examples).
Using a virtual ListView has nothing to do with whether you are using the OS
Shell specifically. You can use a virtual ListView for any purpose. All it
does is allows you to store your own memory for the list item data, rather
than copying the data into the ListView (which is required for non-virtual
ListViews). For example, if you have a TStringList, or other container,
that is storeing your FTP directory information, then you can have the
ListView display that container data directly, rather than requiring you to
manually add items to the TListView and copy the container data to the
TListItem properties for each item.
Quote
I have to confess: I'm not entirely sure how to make a TListView behave
as a virtual ListView, but it seems the OwnerData property needs to be
true.
To use a TListView in virtual mode, there are several steps that you need to
do:
1) set the OwnerData property to True.
2) assign an OnData event handler that assigns the TListItem properties for
a specific TListItem. On entry, the TListItem's Index property is usually
the only value that is assigned yet. You use that indes to match the
TListItem to the cooresponding data in your container.
3) if you have a lot of items, or are performing filtering, then consider
also assigning an OnDataHint event handler so that you can cache the data
more effectively. The event handler will provide the range of ListView
indexes that are most likely to be needed by the OnData event soon, so you
can ensure that those data items are available beforehand.
3) when adding items to the ListView, always use the TListItems::Count
property instead of the TListItems::Add/Insert() methods. Arrange the data
in your container as needed, and then set the Count to the number of items
to display. The OnData and OnDataHint events will be triggered when the
TListView needs list item data from you.
Gambit
 

Re:Re: Getting SYS Icons

Hi Gambit,
Sorry for taking so long to get back with you. Work pulled me away to other
things.
Quote
>Also, it looks like once I add the OnGetImageIndex event handler
>for lvPC (the virtual ListView), the virtual part appears to stop working

Please elaborate.

When GetImageIndex(TObject *Sender, TListItem *Item) is called, the ListView
Item already has an ImageIndex value.
After looking at the help, I saw that I needed to add SHGFI_ICON to the list
of flags, otherwise the FileInfo.iIcon (index) was returning 0.