🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.nativeapi
14 messages
14 total messages Started by poojo hackma Wed, 08 Mar 2006 14:54
BCB6: Getting SYS Icons
#4225
Author: poojo hackma
Date: Wed, 08 Mar 2006 14:54
33 lines
2495 bytes
I've got a post in borland.public.cppbuilder.attachments with the same 
subject line as this, and Bob Gonder suggested that I post my question here. 
This is my first time posting in the nativeapi group, so please be nice.  :)

In that attachment is a simple project that populates a TListView with files 
and folders.  What is unique about it is the way it uses system icons on the 
items: Everything gets the same icon that Windows has registered for it.

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).

I want to say, "Give me my PC's default Windows icon for a .cpp, .doc, .pdf, 
.jpg, etc."  I want the icon that my PC has registered for that particular 
type of file to appear in a TListView.

I have been trying to step through the code in the attachments area 
(news:440d9a95@newsgroups.borland.com) to find out where icons are assigned 
and how they are obtained, but it looks like the example starts with a call 
to SHGetDesktopFolder(LPSHELLFOLDER x).

I tried calling:

SHGetFileInfo(strFilename, FILE_ATTRIBUTE_NORMAL, &fileInfo, 
sizeof(SHFILEINFO), SHGFI_USEFILEATTRIBUTES)

...but I don't understand what to assign the fileInfo.iIcon parameter to 
(TListView->Items->ImageIndex = fileInfo.iIcon?) or when to insert it 
(OnCustomDrawItem?).

Thanks in advance for your help.

Regards,
poojo
Re: BCB6: Getting SYS Icons
#4226
Author: Andrue Cope [TeamB]
Date: Wed, 08 Mar 2006 15:30
87 lines
3388 bytes
poojo hackma wrote:

> 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).
> 
> I want to say, "Give me my PC's default Windows icon for a .cpp,
> .doc, .pdf, .jpg, etc."  I want the icon that my PC has registered
> for that particular type of file to appear in a TListView.

We just use ExtractAssociatedIcon() but reviewing the code suggests
that someone implemented a work around at one point:

[snip]
  {
    WORD iconIndex=0;

    char damnMicrosoft[ MAXPATH+1 ];
    strcpy( damnMicrosoft, FileName.c_str() );
    result=ExtractAssociatedIcon( HInstance,
                                  damnMicrosoft,
                                  &iconIndex );
  }

  return result;
}

AFAIK this is all we do for conventional files local or networked but
we have another type of file that isn't available to windows. It may be
that our solution for that would work for you as well:

  if( IsVirtualF )
  { // Virtual object
    if( !IsADirF )
    {
      char *extZ=strrchr( FileName.c_str(), '.' );

      if( extZ )
      {
        AnsiString ext=extZ;

        Reg->RootKey=HKEY_CLASSES_ROOT;

        {
          if( Reg->OpenKey( "\\"+ext, false ) )
          {
            AnsiString key=Reg->ReadString( "" );
            if( Reg->OpenKey( "\\"+key, false ) )
            {
              if( Reg->OpenKey( "DefaultIcon", false ) )
              {
                AnsiString iconPath=Reg->ReadString("");

                char *commaPosn=strrchr( iconPath.c_str(), ',' );

                AnsiString iconNumber=0;

                if( commaPosn )
                {
                  iconNumber=commaPosn+1;

                  iconPath.SetLength( commaPosn-iconPath.c_str() );
                }

                if( FileExists( iconPath )||
                    iconPath.Pos( "%SystemRoot%" ) )
                {
                  result=ExtractIcon( Application->Handle,
                                      iconPath.c_str(),
                                      iconNumber.ToInt() );
                }
                else
                  ext=".COM";
              }
            }
          }

          Reg->CloseKey();
        }
      }
    }

..and FWIW the significance of that 'ext=".COM";' escapes me. Time for
a little bit of editing methinks :)
-- 
Andrue Cope [TeamB]
[Bicester, Uk]
http://info.borland.com/newsgroups/guide.html
Re: Getting SYS Icons
#4230
Author: Remy Lebeau (TeamB)
Date: Wed, 08 Mar 2006 19:50
40 lines
2464 bytes
"poojo hackma" <poojo.com/mail> wrote in message
news:440ef041$1@newsgroups.borland.com...

> 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;
    }

> 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: Getting SYS Icons
#4143
Author: poojo hackma
Date: Thu, 09 Mar 2006 20:05
6 lines
1292 bytes
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: Getting SYS Icons
#4144
Author: Hans Galema
Date: Thu, 09 Mar 2006 20:28
8 lines
1230 bytes
poojo hackma wrote:

> 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.
Re: Getting SYS Icons
#4145
Author: Remy Lebeau (TeamB)
Date: Thu, 09 Mar 2006 21:33
16 lines
1570 bytes
"poojo hackma" <poojo.com/mail> wrote in message
news:44108aa0$1@newsgroups.borland.com...

> 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: Getting SYS Icons
#4147
Author: poojo hackma
Date: Thu, 09 Mar 2006 22:27
48 lines
2997 bytes
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;
    }
  }
}


> Can you please show how you construct the filename ? Is it something
> with the dir + DisplayName ?
>
> Hans. 
Re: Getting SYS Icons
#4148
Author: Remy Lebeau (TeamB)
Date: Fri, 10 Mar 2006 01:02
93 lines
4277 bytes
"poojo hackma" <poojo.com/mail> wrote in message
news:4410abd3@newsgroups.borland.com...

> 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;
    }

> 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
(http://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: Getting SYS Icons
#4152
Author: Hans Galema
Date: Fri, 10 Mar 2006 11:25
18 lines
1668 bytes
poojo hackma wrote:

> 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: Getting SYS Icons
#4156
Author: poojo hackma
Date: Fri, 10 Mar 2006 14:39
10 lines
1634 bytes
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?  :)

> 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: Getting SYS Icons
#4157
Author: poojo hackma
Date: Fri, 10 Mar 2006 15:49
18 lines
2057 bytes
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: Getting SYS Icons
#4160
Author: Hans Galema
Date: Fri, 10 Mar 2006 18:43
9 lines
1382 bytes
poojo hackma wrote:

> 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: Getting SYS Icons
#4161
Author: Remy Lebeau (TeamB)
Date: Fri, 10 Mar 2006 19:18
50 lines
3413 bytes
"poojo hackma" <poojo.com/mail> wrote in message
news:4411a01a@newsgroups.borland.com...

> 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.

> 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.

> 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: Getting SYS Icons
#4177
Author: poojo hackma
Date: Mon, 13 Mar 2006 16:28
16 lines
1795 bytes
Hi Gambit,

Sorry for taking so long to get back with you.  Work pulled me away to other 
things.

>> 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.
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads